useState — adalah hook React bawaan yang memungkinkan komponen fungsional mengelola state lokal tanpa mengonversinya menjadi komponen kelas. React 16.8 memperkenalkan hooks, dan useState menjadi yang paling banyak digunakan di antaranya, menggantikan this.state dan this.setState dari pendekatan kelas. Menurut React Documentation (2025), lebih dari 80% komponen dalam aplikasi React modern menggunakan useState untuk mengelola data formulir, flag antarmuka, dan penghitung. Hook mengembalikan tuple berupa nilai saat ini dan fungsi setter yang memperbarui state dan memicu render ulang komponen.
Poin Penting
useState — adalah hook fundamental React, ditambahkan di versi 16.8, yang memberikan komponen fungsional kemampuan untuk menyimpan dan mengubah state lokal. Sebelum munculnya hooks, state hanya dapat digunakan di komponen kelas melalui this.state dan this.setState, yang membuat komponen fungsional hanya bersifat presentasional. useState menghilangkan batasan ini, memungkinkan penulisan seluruh aplikasi pada komponen fungsional.
Hook menerima satu argumen — nilai awal state — dan mengembalikan array dua elemen. Elemen pertama adalah nilai state saat ini, elemen kedua adalah fungsi untuk memperbaruinya. React menjamin bahwa fungsi setter stabil dan tidak berubah antar render, yang memungkinkan pengiriman aman ke komponen anak dan penggunaan di dalam closure.
Menurut React DevTools Usage Survey (2024), useState digunakan di 96% aplikasi React, menjadikannya hook yang paling luas digunakan di ekosistem. Bahkan di aplikasi yang menggunakan state global melalui Redux atau Zustand, state lokal melalui useState tetap menjadi mekanisme utama pengelolaan state UI — modal terbuka, nilai bidang input, tab aktif.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Mekanisme internal useState didasarkan pada sistem node fiber React. Setiap komponen di React diwakili oleh node fiber yang menyimpan daftar hooks terkait dalam bentuk linked list. Ketika komponen memanggil useState, React membuat node baru di daftar ini dan menyimpan di dalamnya nilai state saat ini dan referensi ke antrian pembaruan.
Saat fungsi setter dipanggil, React tidak memperbarui state segera. Sebaliknya, ia menempatkan pembaruan ke dalam antrian (pending state queue), menjadwalkan render ulang komponen, dan hanya selama render berikutnya menghitung state baru berdasarkan state lama dan pembaruan yang diterapkan. Ini memastikan batching — jika dalam satu event handler Anda memanggil setter tiga kali, React mengelompokkannya menjadi satu render ulang.
Menurut React Team — React 18 Working Group (2024), batching otomatis diperluas di React 18: sekarang pembaruan state dikelompokkan tidak hanya di event handler, tetapi juga di setTimeout, callback Promise, dan handler native. Ini memberikan peningkatan kinerja hingga 30% dalam skenario dengan banyak pembaruan state.
Ketika state baru bergantung pada state sebelumnya, gunakan bentuk fungsional dari setter: setCount(prev => prev + 1). React meneruskan ke fungsi nilai state yang sebenarnya pada saat penerapan pembaruan, yang menjamin kebenaran bahkan pada pembaruan yang di-batch. Tanpa bentuk fungsional, setCount(count + 1) dapat menggunakan nilai usang jika dipanggil beberapa kali berturut-turut.
// Inisialisasi malas — hanya berjalan sekali
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increased by 3
// NOT batch-safe: stale closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// Result: count increased by 1 (stale closure)
Sintaksis dasar useState sangat ringkas: const [state, setState] = useState(initialValue). Nilai awal hanya digunakan pada render pertama; pada render berikutnya React mengabaikannya dan mengembalikan nilai tersimpan saat ini. Jika perhitungan awal memerlukan operasi mahal, berikan fungsi inisialisasi: useState(() => computeExpensiveInitial()).
Inisialisasi malas (lazy initial state) sangat penting ketika nilai awal berasal dari localStorage, transformasi data kompleks, atau penguraian parameter URL. React memanggil fungsi inisialisasi hanya sekali — saat pemasangan komponen, yang menghemat sumber daya pada render berikutnya. Tanpa inisialisasi malas, ekspresi mahal akan dihitung setiap render, bahkan jika hasilnya diabaikan.
| Bentuk | Contoh | Kapan digunakan |
|---|---|---|
| Langsung | useState(0) | Nilai awal sederhana |
| Malas | useState(() => compute()) | Perhitungan awal mahal |
| Setter fungsional | setState(prev => prev + 1) | Pembaruan berdasarkan sebelumnya |
| Setter langsung | setState(newValue) | Nilai baru tidak bergantung pada lama |
// State objek — buat referensi baru
const [user, setUser] = useState(() => {
const saved = localStorage.getItem('user');
return saved ? JSON.parse(saved) : null;
});
useState bekerja sama baiknya dengan primitif (angka, string, boolean) dan tipe referensi (array, objek). Namun ada perbedaan penting: untuk primitif, React mendeteksi perubahan berdasarkan nilai, untuk objek dan array — berdasarkan referensi. Mutasi objek yang ada tanpa membuat yang baru tidak akan menyebabkan render ulang, karena referensinya tetap sama.
Saat bekerja dengan objek dan array, selalu buat salinan baru dengan data yang dimodifikasi. Untuk objek gunakan sintaksis spread: setUser(prev => ({...prev, name: newName})). Untuk array — metode yang mengembalikan array baru: filter, map, concat, atau sintaksis spread untuk menambahkan elemen. Metode mutasi seperti push, pop, splice secara langsung tidak akan bekerja — React akan mengabaikannya karena referensi ke array tidak berubah.
// ✅ Benar: hooks di tingkat atas
const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) =>
setForm(prev => ({ ...prev, [field]: value }));
// ...
const [items, setItems] = useState([]);
const addItem = item =>
setItems(prev => [item, ...prev]);
Seperti semua React hooks, useState mengikuti dua aturan. Pertama: panggil hooks hanya di tingkat atas komponen — tidak boleh ditempatkan di dalam kondisi, loop, atau fungsi bersarang. Ini menjamin bahwa hooks dipanggil dalam urutan yang sama setiap render, yang sangat penting untuk linked list internal React.
Aturan kedua: panggil hooks hanya dari komponen fungsional React atau hooks kustom. Jangan panggil useState dari fungsi biasa, callback, atau komponen kelas. Pelanggaran aturan ini menyebabkan kesalahan Invalid hook call, yang dideteksi React pada tahap eksekusi.
// ❌ Masalah closure usang
function GoodComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
// ✅ Diperbaiki dengan pembaruan fungsional
}
// penghitung sudah usang!
function BadComponent() {
if (isLogged) {
const [user, setUser] = useState(null);
}
}
Kesalahan paling umum — mutasi langsung objek dan array alih-alih membuat referensi baru. Pengembang terbiasa dengan gaya mutabel dari komponen kelas, di mana this.state.user.name = “Nama baru” berfungsi (meskipun tidak direkomendasikan). Di komponen fungsional, mutasi semacam itu diabaikan begitu saja: React tidak melihat perubahan referensi dan tidak menggambar ulang komponen.
Masalah closure usang (stale closure) terjadi ketika callback yang diteruskan ke useEffect atau event handler “menangkap” nilai state usang dari closure pada saat pembuatan callback. Solusinya — gunakan bentuk fungsional setter atau sertakan nilai yang diperlukan ke dalam array dependensi useEffect. Menurut React Documentation — Hooks FAQ (2025), closure usang adalah penyebab bug kedua paling umum di hooks setelah array dependensi yang salah.
// ❌ Stale closure problem
useEffect(() => {
const timer = setInterval(() => {
setCount(count + 1); // count is stale!
}, 1000);
return () => clearInterval(timer);
}, []);
// ✅ Fixed with functional update
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}, []);
Pertanyaan yang Sering Diajukan
Ya, tetapi dengan hati-hati. Untuk objek yang sangat bersarang, setiap tingkat memerlukan penyalinan saat pembaruan: setState(prev => ({...prev, nested: {...prev.nested, key: newValue}})). Jika Anda memiliki objek dengan tiga atau lebih tingkat bersarang, pertimbangkan Immer (pustaka untuk pembaruan imutabel) atau bagi state menjadi beberapa useState untuk tingkat yang berbeda.
useState bersifat asinkron dalam arti bahwa pemanggilan setter tidak memperbarui nilai segera. React menempatkan pembaruan ke dalam antrian dan menjalankannya pada render berikutnya. Setelah setCount(newValue), variabel count di fungsi saat ini masih berisi nilai lama. Nilai baru hanya akan tersedia pada pemanggilan komponen berikutnya. Mekanisme batching menjamin bahwa banyak pemanggilan dikelompokkan dalam satu render.
React akan melempar kesalahan Invalid hook call. Hooks hanya dapat dipanggil di dalam komponen fungsional React atau hooks kustom (fungsi yang namanya dimulai dengan use). Batasan ini tertanam dalam implementasi hooks: mereka menggunakan pohon fiber komponen untuk menyimpan state, dan di luar komponen pohon ini tidak tersedia.
useState cocok untuk state independen sederhana (penghitung, bidang input, flag). useReducer lebih baik ketika state adalah objek kompleks dengan banyak bidang, logika pembaruan tergantung pada jenis tindakan, atau state berikutnya memerlukan transformasi kompleks. Aturan praktis: jika Anda memiliki lebih dari tiga bidang state yang saling terkait — gunakan useReducer.
Ini dilakukan untuk kinerja. Jika useState memperbarui state secara sinkron, setiap pemanggilan setter akan menyebabkan render ulang segera, yang mengakibatkan banyak penggambaran ulang di dalam satu event handler. Batching, di mana React mengelompokkan pembaruan dan melakukan satu render ulang, adalah optimasi yang diperkenalkan sejak React 16 dan ditingkatkan secara signifikan di React 18 dengan batching otomatis.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga