Bayangin form login dengan komponen <EmailInput> di kiri dan <SubmitButton> di kanan. Pas user ngetik email belum valid, button harus disabled. Tapi <SubmitButton> gak tahu apa email valid — itu state di dalam <EmailInput>. Cara React nyelesaiin: angkat state ke parent terdekat yang nge-host kedua komponen. Email state pindah ke parent → di-pass ke <EmailInput> (untuk display) dan ke <SubmitButton> (untuk cek valid). State sentralisasi di satu tempat = "single source of truth".
💡 Pattern fundamental. Lifting state up adalah salah satu pattern PALING SERING dipake di React. Tiap kali 2+ komponen butuh sync data — naikkan state ke parent. Lawan dari pattern ini: prop drilling (state di terlalu atas, drilled lewat banyak level) — solusi: Context API (lesson selanjutnya).
function Display({ value }) {
return <span>{value}</span>
}
function Controls({ onIncrement }) {
return <button onClick={onIncrement}>+1</button>
}
function App() {
// State diangkat ke parent — Display + Controls share count
const [count, setCount] = useState(0)
return (
<div>
<Display value={count} />
<Controls onIncrement={() => setCount(c => c + 1)} />
</div>
)
}
Pattern Lengkap
function TempInput({ scale, value, onChange }) {
return (
<div>
<label>Suhu dalam {scale}: </label>
<input value={value} onChange={(e) => onChange(e.target.value)} />
</div>
)
}
function Calculator() {
// STATE LIFTED — kedua input share state
const [temperature, setTemperature] = useState("")
const [scale, setScale] = useState("c")
return (
<div>
<TempInput
scale="Celsius"
value={scale === "c" ? temperature : convertCtoF(temperature)}
onChange={(t) => { setScale("c"); setTemperature(t) }}
/>
<TempInput
scale="Fahrenheit"
value={scale === "f" ? temperature : convertFtoC(temperature)}
onChange={(t) => { setScale("f"); setTemperature(t) }}
/>
</div>
)
}
Ketik di input Celsius → state update → input Fahrenheit otomatis convert + display. Beautifully synced.
Prinsip
- Identifikasi state yang dibutuhkan bersama — komponen mana yang akses?
- Cari parent TERDEKAT yang nge-host semua komponen tersebut
- Pindahkan state ke parent — pakai useState/useReducer di parent
- Pass DATA via props ke bawah —
<Child value={state} /> - Pass FUNCTION via props untuk update —
<Child onChange={setState} />
🎭 Analogi sehari-hari
Lifting state itu kayak dapur bersama di kontrakan. Dulu tiap kamar punya dapur sendiri-sendiri (state lokal di tiap komponen) — boros, ribet sinkronisasi (kalau kamar A masak nasi, kamar B gak otomatis tau). Renovasi: pindahin dapur ke ruang tengah (parent), semua kamar share dapur yang sama. Si A masak → si B liat panci di kompor → tahu nasi udah siap. Single source of truth: satu nasi, satu kompor, semua kamar paham. Plus efisien — gak perlu duplikasi peralatan masak. Risiko: ruang tengah (parent) kepenuhan kalo terlalu banyak masak — itu sinyal extract ke "dapur regional" (Context API).
⚠️ Jebakan yang sering ditemui
- Lifting terlalu tinggi — state di App.tsx tapi cuma dipake 2 sibling component dalam dalam. Naikin secukupnya — ke parent TERDEKAT.
- Lifting terlalu rendah — state di salah satu sibling, sibling lain butuh akses tapi gak bisa. Naikin lebih atas.
- Forgetting to lift — duplicate state di sibling, sync manual via useEffect = anti-pattern. Lift up + derive.
- Pass setter langsung ke child —
<Input onChange={setEmail}>OK simple, tapi child jadi tau persis API state. Lebih clean: wrap dengan handler di parent. - Drill state lebih dari 3 level = pain. Sinyal: pakai Context API atau state management library.
- Lift state buat sibling yang DEEP nested — 3+ level — banyak komponen perantara cuma jadi pipa. Context lebih cocok.
- Mutate state di child via callback — child terima
data(read-only) +onUpdate(newData)(update via parent). JANGAN child mutatedatalangsung.
🎯 State di mana — local atau lifted?
- Cuma 1 komponen pakai → state lokal (
useStatedi komponen itu)- 2+ sibling komponen pakai → lift ke parent terdekat
- Many distant components pakai (3+ level) → Context API (atau state management library)
- State global (theme, user, lang) → Context API
- Server state (data dari API) → TanStack Query (cache + sync built-in)
- URL state (filter, page, search) → URL params (react-router atau next router)
Aturan: state di posisi paling DEKAT dengan yang butuh. Lift secukupnya. Naikin ke Context cuma kalau drilling > 2-3 level.
TL;DR: Lifting state up = pindahin shared state ke komponen parent terdekat yang nge-host komponen yang butuh. Pass DATA ke child via props (read-only), pass FUNCTION untuk update. "Single source of truth" — sync otomatis antar sibling. Lifting terlalu tinggi = banyak prop drilling. Lifting terlalu rendah = duplicate state di sibling. > 3 level drilling = waktunya Context API.