Fundamental React Hooks: useState, useEffect, useRef, useContext
Dokumentasi ini menjelaskan konsep dasar Hooks di React — cara komponen menyimpan dan mengelola data yang bisa berubah.
Kenapa React Butuh "State"?
Di React, komponen adalah function biasa yang me-return JSX. Tapi function biasa tidak bisa "ingat" nilai antar render — setiap kali function dipanggil ulang, semua variabel biasa di dalamnya di-reset dari awal.
Masalahnya: sering dibutuhkan komponen yang berubah tampilannya berdasarkan interaksi user (klik tombol, isi form, dll), dan nilai itu harus tetap tersimpan di antara render-render tersebut. Di sinilah state berperan — dan useState adalah cara paling dasar untuk membuatnya.
1. useState — Menyimpan Nilai yang Bisa Berubah
import { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>Jumlah: {count}</p>
<button onClick={() => setCount(count + 1)}>Tambah</button>
</div>
)
}
Cara bacanya:
useState(0)→ nilai awal-nya0count→ nilai state saat ini (read-only, jangan diubah langsung)setCount→ fungsi khusus untuk mengubah nilaicount
Poin paling penting: setiap kali setCount dipanggil, React akan render ulang komponen itu dengan nilai count yang baru. Kalau count = count + 1 diubah manual tanpa setCount, React tidak akan tahu ada perubahan, dan tampilan tidak akan ter-update.
Kenapa Tidak Pakai Variabel Biasa?
// ❌ SALAH — tidak akan bekerja
function Counter() {
let count = 0
return (
<div>
<p>Jumlah: {count}</p>
<button onClick={() => count = count + 1}>Tambah</button>
</div>
)
}
Ini gagal karena:
- Mengubah
let counttidak memberi tahu React untuk render ulang - Setiap kali komponen di-render ulang (dari luar),
countakan kembali ke0
useState menyelesaikan dua masalah ini sekaligus: menyimpan nilai antar render + memicu render ulang otomatis saat nilainya berubah.
2. useEffect — Menjalankan Kode Setelah Render (Efek Samping)
Dipakai kalau dibutuhkan sesuatu di luar proses render biasa — misalnya fetch data dari API, pasang event listener, atau update judul halaman.
import { useState, useEffect } from 'react'
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then((data) => setUser(data))
}, [userId]) // <- dependency array
if (!user) return <p>Memuat...</p>
return <p>Halo, {user.name}</p>
}
Bagian [userId] di akhir disebut dependency array — artinya "jalankan ulang effect ini hanya kalau userId berubah." Beberapa pola pentingnya:
| Dependency Array | Kapan Effect Dijalankan |
|---|---|
useEffect(() => {...}) (tanpa array) |
Setiap kali komponen render — jarang dipakai, bisa infinite loop kalau ada setState di dalamnya |
useEffect(() => {...}, []) |
Hanya sekali, saat komponen pertama kali muncul |
useEffect(() => {...}, [userId]) |
Setiap kali userId berubah nilainya |
Catatan penting untuk Next.js: di App Router, kalau sudah pakai Server Component (seperti route /users dan /roles yang langsung await prisma.findMany()), useEffect + useState untuk fetch data awal tidak dibutuhkan — itu tugasnya kalau kodenya jalan di Client Component (butuh 'use client' di baris paling atas file).
3. useRef — Menyimpan Nilai Tanpa Memicu Render Ulang
Mirip useState, tapi tidak membuat komponen render ulang saat nilainya berubah. Biasa dipakai untuk:
- Akses langsung ke elemen HTML (misal fokus ke input)
- Menyimpan nilai yang perlu "diingat" tapi tidak perlu ditampilkan
import { useRef } from 'react'
function TextInput() {
const inputRef = useRef(null)
const focusInput = () => {
inputRef.current.focus()
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Fokus ke Input</button>
</div>
)
}
4. useContext — Berbagi Data Tanpa "Prop Drilling"
Dipakai kalau banyak komponen bertingkat butuh data yang sama (misal data user yang sedang login), tanpa harus melempar props manual dari komponen induk ke turunannya satu per satu ("prop drilling").
import { createContext, useContext } from 'react'
const UserContext = createContext(null)
function App() {
return (
<UserContext.Provider value={{ name: 'Andi' }}>
<Dashboard />
</UserContext.Provider>
)
}
function Dashboard() {
const user = useContext(UserContext)
return <p>Halo, {user.name}</p>
}
Tabel Ringkasan
| Hook | Fungsi |
|---|---|
useState |
Menyimpan nilai yang bisa berubah dan memicu render ulang |
useEffect |
Menjalankan efek samping (fetch data, event listener) setelah render |
useRef |
Menyimpan nilai/akses elemen tanpa memicu render ulang |
useContext |
Berbagi data antar komponen tanpa prop drilling |
Aturan Penting Soal Hooks
- Hooks hanya bisa dipakai di Client Component — file harus punya
'use client'di baris paling atas (kecuali memakai Server Component murni tanpa interaktivitas) - Tidak boleh dipanggil di dalam kondisi/loop — harus selalu di level paling atas function komponen
// ❌ SALAH
if (someCondition) {
const [state, setState] = useState(0)
}
// ✅ BENAR
const [state, setState] = useState(0)
if (someCondition) {
// pakai state di sini
}