Kadang dua komponen bersaudara (sibling, anak dari parent yang sama) harus menampilkan data yang sama. Kalau masing-masing menyimpan state sendiri, mereka gak akan pernah sepakat. Solusinya: pindahkan state itu ke parent terdekat yang membungkus keduanya, lalu turunkan ke anak-anaknya lewat props. Teknik ini namanya lifting state up, mengangkat state ke atas. Bayangin dua kasir di satu warung yang mencatat stok di buku masing-masing. Kasir A menjual roti terakhir, tapi buku kasir B masih bilang ada satu. Warung yang rapi menaruh satu buku stok di meja tengah, dan kedua kasir membaca dan menulis di buku yang sama.
🧪 Tebakan cepat:
NamaInputdanSapaanmasing-masing punyauseState('Tamu'). Kamu mengetik "Rina" diNamaInput.Sapaanmenampilkan "Halo, Rina!" atau tetap "Halo, Tamu!"? Tebak dulu, jawabannya ada di bagian "Dua saudara yang gak sepakat".
Dua saudara yang gak sepakat
function NamaInput() {
const [nama, setNama] = useState('Tamu')
return <input value={nama} onChange={(e) => setNama(e.target.value)} />
}
function Sapaan() {
const [nama] = useState('Tamu') // state milik Sapaan sendiri
return <p>Halo, {nama}!</p>
}
function App() {
return (
<div>
<NamaInput />
<Sapaan />
</div>
)
}
Kamu mengetik "Rina". State nama milik NamaInput jadi 'Rina', tapi Sapaan punya state sendiri yang tetap 'Tamu'. Hasilnya "Halo, Tamu!". Itu jawaban tebakannya. Di React, data cuma mengalir satu arah, dari parent ke anak lewat props. Ini disebut one-way data flow. Saudara gak bisa membaca state saudaranya, jadi Sapaan gak pernah tahu ada ketikan baru.
Angkat state ke parent terdekat
function NamaInput({ nama, onNamaChange }) {
return <input value={nama} onChange={(e) => onNamaChange(e.target.value)} />
}
function Sapaan({ nama }) {
return <p>Halo, {nama}!</p>
}
function App() {
const [nama, setNama] = useState('Tamu') // satu-satunya tempat nama disimpan
return (
<div>
<NamaInput nama={nama} onNamaChange={setNama} />
<Sapaan nama={nama} />
</div>
)
}
- State
namasekarang cuma ada diApp, parent terdekat yang membungkus kedua anak. Satu data, satu tempat. Istilahnya single source of truth. - Data turun lewat props:
nama={nama}dikirim ke dua-duanya. Anak cuma membaca props, gak mengubahnya. - Perubahan naik lewat function:
AppmemberionNamaChange={setNama}, danNamaInputmemanggilonNamaChange(e.target.value)tiap kali diketik. Awalanoncuma kebiasaan untuk prop yang isinya function.
| Langkah | Yang terjadi |
|---|---|
| 1 | Kamu mengetik sampai kolomnya berisi "Rina", onChange di NamaInput jalan |
| 2 | NamaInput memanggil onNamaChange('Rina'), yaitu setNama milik App |
| 3 | State nama di App jadi 'Rina', dan App render ulang |
| 4 | App mengirim nama yang baru ke NamaInput dan Sapaan, keduanya render ulang |
| 5 | Sapaan menampilkan "Halo, Rina!" |
Pola yang sama untuk angka
function Display({ value }) {
return <span id="display">{value}</span>
}
function Controls({ onIncrement }) {
return <button id="add" onClick={onIncrement}>+1</button>
}
function App() {
const [count, setCount] = useState(0)
return (
<div>
<Display value={count} />
<Controls onIncrement={() => setCount((c) => c + 1)} />
</div>
)
}
Displaycuma menampilkan propvalue.Controlscuma punya tombol yang memanggil proponIncrement. Dua-duanya gak menyimpan angka.counttinggal diApp, mulai dari 0. Klik+1sekali,#displayjadi 1. Klik dua kali, jadi 2.() => setCount((c) => c + 1)adalah function yang dikirim, bukan dijalankan saat render. Updater(c) => c + 1menambah dari nilai terbaru, jadi dua klik beruntun tetap jadi 2.
Kapan dan bagaimana mengangkat
Angkat state saat dua komponen atau lebih butuh data yang sama. Kalau cuma satu komponen yang memakainya, biarkan state-nya lokal di komponen itu. Langkahnya:
- Cari komponen mana saja yang butuh data itu.
- Cari parent terdekat yang membungkus semuanya.
- Pindahkan
useStateke parent itu. - Turunkan nilainya lewat props (
value,nama). - Turunkan function untuk mengubahnya lewat props (
onIncrement,onNamaChange).
⚠️ Jebakan yang sering ketemu
- Menyalin props ke state anak.
const [nama, setNama] = useState(props.nama)di anak cuma memakai nilai awalnya sekali, lalu anak berhenti mengikuti parent. Pakai props-nya langsung. - Anak mengubah props sendiri.
props.nama = 'Rina'gak memicu render. Anak meminta perubahan dengan memanggil function dari parent. - Memanggil function-nya waktu dikirim.
onIncrement={setCount(count + 1)}langsung jalan saat render, dan render lagi tanpa henti. Kirim function-nya:onIncrement={() => setCount((c) => c + 1)}. - Mengangkat terlalu tinggi. State yang cuma dipakai dua anak di satu bagian kecil gak perlu sampai ke komponen paling atas. Makin tinggi, makin banyak komponen di tengah yang cuma meneruskan props (prop drilling). Angkat secukupnya, ke parent terdekat.
TL;DR: Saat dua komponen butuh data yang sama, angkat state-nya ke parent terdekat yang membungkus keduanya. Teknik ini namanya lifting state up. Parent menyimpan state dengan useState, menurunkan nilainya lewat props (value={count}, nama={nama}), dan menurunkan function untuk mengubahnya (onIncrement, onNamaChange). Anak memanggil function itu, misalnya onNamaChange(e.target.value), parent mengganti state, lalu semua anak render ulang dengan nilai yang sama. Data mengalir satu arah, dari atas ke bawah. State yang cuma dipakai satu komponen tetap lokal.