Reactjs State Management Local Lifted Context Reducer External Store Basics
Definisi
State management di React bukan cuma soal “taruh data di mana”. Intinya adalah menentukan siapa pemilik state, siapa yang boleh mengubahnya, dan bagaimana perubahan itu mengalir ke UI.
Lima pola yang paling sering dipakai adalah:
Local state: state hidup di komponen yang memakainya.
Lifted state: state dipindahkan ke parent terdekat yang perlu membaginya ke beberapa child.
Context: state atau value dibagikan ke banyak komponen di subtree tanpa prop drilling.
Reducer: state dikelola lewat aksi dan fungsi reducer yang deterministik.
External store: state hidup di luar tree React, lalu React membaca snapshot-nya melalui mekanisme subscription.
Urutan di atas bukan urutan “naik kelas”. Yang benar adalah: pakai yang paling kecil yang masih bisa menyelesaikan masalah dengan jelas.
Gambaran ownership dan update flow
Loading diagram...
Diagram ini sengaja menunjukkan satu hal penting: yang berubah bukan cuma data, tetapi juga radius dampaknya. Semakin tinggi state dinaikkan, semakin besar area yang ikut tahu perubahan itu.
Local state
Local state adalah pilihan default untuk state yang hanya relevan di satu komponen atau subtree kecil.
Kapan dipakai
Pakai local state kalau:
nilai hanya dipakai oleh komponen itu sendiri,
perubahan nilainya hanya memengaruhi UI lokal,
state tidak perlu dibaca komponen lain,
logika masih sederhana dan langsung terbaca.
Contoh tipikal:
input kecil,
toggle show/hide,
tab aktif di satu panel,
loading state lokal,
dialog open/close.
Kelebihan
paling sederhana,
paling eksplisit,
mudah di-debug,
rerender biasanya terlokalisasi,
mudah dihapus kalau fitur berubah.
Trade-off
sulit dibagi kalau kebutuhan berkembang,
bisa memicu prop drilling kalau mulai banyak child perlu data yang sama,
state dan view logic bisa menumpuk kalau komponen dibuat terlalu besar.
Kalau state ini hanya dipakai di SearchPage dan turunannya, lifting state cukup. Tidak perlu Context hanya karena ada dua consumer.
Context
Context dipakai untuk membagikan value ke banyak komponen di bawah provider tanpa meneruskan props di tiap level.
Kapan dipakai
Pakai Context kalau:
banyak komponen di subtree yang sama butuh data yang sama,
data itu bersifat cross-cutting untuk satu area UI,
prop drilling mulai mengganggu struktur,
nilainya lebih cocok dianggap shared app state daripada state lokal.
Contoh yang sering cocok:
theme,
locale,
authenticated user,
feature flags tertentu,
layout preferences untuk satu subtree.
Kelebihan
menghilangkan prop drilling,
akses lebih nyaman dari banyak level,
bagus untuk data yang memang shared di area tertentu.
Trade-off
consumer menjadi implisit,
rerender bisa menyebar ke banyak pembaca,
provider mudah jadi “drawer serbaguna” yang menampung terlalu banyak hal,
testing sering butuh setup provider tambahan.
Alur Context
Loading diagram...
Jebakan Context yang terlalu cepat dibesarkan
Ini jebakan yang sering muncul:
Ada state kecil, misalnya isOpen.
Karena “mungkin nanti dipakai banyak tempat”, state itu langsung dipindah ke Context.
Provider ditempatkan terlalu tinggi.
Consumer jadi banyak, walau kebutuhannya sebenarnya lokal.
Scope state melebar tanpa alasan nyata.
Masalahnya bukan Context itu sendiri. Masalahnya adalah mengubah state lokal menjadi shared state sebelum ada bukti kebutuhan.
Kalau Context mulai menampung banyak hal yang tidak saling berkaitan, biasanya tanda desainnya sudah kebablasan.
Reducer
Reducer adalah pola pengelolaan state yang memisahkan event intent dari perhitungan state baru.
Di React, pola ini biasa dipakai lewat useReducer, tetapi idenya lebih umum: komponen mengirim aksi, reducer menghitung state berikutnya.
Kapan dipakai
Pakai reducer kalau:
state punya banyak kemungkinan transisi,
update state tergantung aksi yang berbeda-beda,
beberapa field harus berubah secara konsisten dalam satu langkah,
logika update mulai sulit dirawat dengan beberapa useState terpisah,
kamu ingin alur update lebih mudah ditest.
Contoh yang cocok:
wizard multi-step,
form kompleks,
status async yang punya banyak cabang,
editor dengan banyak aksi user,
state machine ringan.
Kelebihan
update state lebih terpusat,
transisi state lebih mudah di-review,
bagus untuk debugging aksi demi aksi,
cocok saat banyak perubahan harus tetap konsisten.
Trade-off
boilerplate lebih banyak daripada useState,
reducer yang terlalu besar bisa jadi “mini framework” sendiri,
tidak otomatis menyelesaikan problem berbagi state lintas tree,
tetap perlu diputuskan apakah state-nya lokal, lifted, atau shared.
Contoh
jsx
import { useReducer } from 'react';const initialState = { count: 0 };function reducer(state, action) { switch (action.type) { case 'increment':
Cara membaca reducer
Reducer yang sehat biasanya punya karakter ini:
action bernama jelas,
input state + action menghasilkan output baru,
tidak menyembunyikan side effect,
lebih dekat ke fungsi transformasi daripada tempat logika UI.
Kalau reducer mulai berisi fetch, timer, atau mutation side effect, biasanya desainnya perlu dipisah.
External store basics
External store adalah state yang tidak disimpan di dalam tree React, melainkan di objek store terpisah. React hanya membaca snapshot dan berlangganan perubahan.
Ini dasar yang sering dipakai oleh library seperti Zustand, Redux, Jotai-style store tertentu, atau store custom berbasis subscription.
Kapan dipakai
Pakai external store kalau:
state dibutuhkan oleh banyak area aplikasi,
Context mulai terasa berat atau terlalu sering rerender,
butuh mekanisme subscribe yang lebih terkontrol,
state shared harus diakses lintas subtree tanpa provider chain yang panjang,
kamu ingin pemisahan yang tegas antara data store dan UI tree.
Kelebihan
cocok untuk shared state yang lebih luas,
subscription bisa lebih selektif,
bisa mengurangi prop drilling dan provider nesting,
sering lebih enak untuk state aplikasi yang memang global.
Trade-off
menambah dependensi konsep dan tooling,
debugging perlu memahami store di luar React,
bisa jadi terlalu cepat dipakai untuk kasus kecil,
desain selector/subscription yang buruk tetap bisa bikin rerender boros.
Gambaran sederhana
Loading diagram...
Catatan penting
External store bukan pengganti semua state React. Kalau state hanya dipakai satu komponen, memindahkannya ke store justru menambah jarak antara masalah dan solusinya.
Kapan memilih pola yang mana
Gunakan urutan keputusan ini:
Mulai dari local state.
Kalau sibling perlu berbagi, lift state ke parent terdekat.
Kalau banyak komponen dalam subtree butuh value yang sama, pertimbangkan Context.
Kalau transisi state kompleks dan perlu aksi yang konsisten, gunakan reducer.
Kalau shared state meluas lintas subtree dan subscription perlu lebih terkontrol, lihat external store.
Aturan praktis
Jika state belum menyebar, jangan dipindah.
Jika satu parent masih cukup, jangan lompat ke Context.
Jika banyak aksi mengubah state yang sama, reducer sering lebih rapi.
Jika aplikasi mulai terasa punya shared state lintas banyak area, external store layak dipertimbangkan.
Trade-off ringkas
Pola
Cocok untuk
Kelebihan
Risiko
Local state
UI kecil, kebutuhan lokal
sederhana, eksplisit
sulit dibagi kalau scope membesar
Lifted state
sibling / subtree kecil
source of truth jelas
parent bisa gemuk
Context
shared value dalam subtree
hilangkan prop drilling
rerender meluas, consumer implisit
Reducer
transisi state kompleks
update terpusat, mudah ditest
boilerplate, bisa jadi terlalu besar
External store
shared app state luas
subscription terkontrol
konsep & tooling lebih berat
Jebakan umum saat state terlalu cepat dibesarkan
Beberapa tanda state dibesarkan terlalu cepat:
provider atau store dibuat sebelum kebutuhan nyata muncul,
satu state kecil diperlakukan seperti global state,
banyak komponen bergantung pada state yang sebenarnya hanya relevan di satu fitur,
alur debugging jadi lebih jauh karena ownership tidak lagi dekat dengan UI,
testing makin sulit karena setup meluas.
Prinsip aman yang paling berguna:
Naikkan state hanya sejauh perlu, bukan sejauh mungkin.
Contoh keputusan praktis
Kasus 1: Toggle panel
Tetap local state. Tidak ada alasan untuk Context atau store.
Kasus 2: Filter dan hasil daftar
Biasanya lifted state ke parent container sudah cukup.
Kasus 3: Theme dan session user
Context masuk akal karena banyak consumer di subtree membacanya.
Kasus 4: Form kompleks dengan banyak aksi
Reducer sering lebih nyaman daripada banyak useState yang saling bergantung.
Kasus 5: State lintas halaman atau banyak subtree
Pertimbangkan external store kalau Context mulai terasa terlalu luas atau terlalu berlapis.
Ringkasan singkat
Local state untuk kebutuhan lokal.
Lifted state untuk berbagi di parent-child terdekat.
Context untuk shared value lintas subtree.
Reducer untuk transisi state yang kompleks dan konsisten.
External store untuk shared state yang lebih luas dan subscription-driven.
Kalau ragu, mulai dari yang paling dekat dengan UI. Dalam React, state yang terlalu cepat dibesarkan biasanya lebih mahal daripada state yang dibiarkan kecil sampai memang butuh naik.