Pernah nggak kamu punya data yang dibutuhkan di banyak komponen berbeda, tapi kamu harus lempar-lempar data itu lewat props dari komponen paling atas sampai paling bawah? Itulah yang disebut prop drilling — dan percayalah, ini bisa bikin kode kamu mirip pipa ledeng yang bocor di mana-mana. Di sinilah useContext dan useReducer React global state hadir sebagai penyelamat.
Di artikel ke-10 dari Seri Belajar React: React From Zero to Zorro ini, kita akan belajar cara mengelola state secara global dan terstruktur — tanpa library tambahan seperti Redux — cukup dengan dua senjata bawaan React yang powerful ini. Siap naik level? 🚀
🧠 Apa Itu useContext? Kenalan Dulu Sebelum Pakai
Bayangkan kamu tinggal di apartemen 20 lantai. Kamu ingin mengirim titipan ke penghuni lantai 15, tapi paketnya harus dipegang bergantian oleh setiap penghuni dari lantai 1 sampai 15 — padahal mereka sama sekali tidak butuh isi paketnya! Konyol, kan? Itulah analogi dari prop drilling.
useContext adalah lift-nya. Kamu bisa langsung mengirim "paket" data ke komponen manapun tanpa harus melewati setiap komponen di tengah. React Context menciptakan sebuah toko data global yang bisa diakses komponen mana saja yang mendaftar sebagai pelanggannya.
useContext() = Kartu Anggota untuk Akses Toko
🛠️ Cara Menggunakan useContext — Step by Step
createContext()import { createContext } from 'react'; // Buat context-nya di sini export const ThemeContext = createContext(null);
Providerimport { useState } from 'react'; import { ThemeContext } from './context/ThemeContext'; import Navbar from './components/Navbar'; function App() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <Navbar /> {/* komponen lain bisa akses theme tanpa props! */} </ThemeContext.Provider> ); } export default App;
useContext()import { useContext } from 'react'; import { ThemeContext } from '../context/ThemeContext'; function Navbar() { // Ambil data langsung dari Context — tanpa props! const { theme, setTheme } = useContext(ThemeContext); return ( <nav> <span>Mode: {theme}</span> <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> Toggle Theme </button> </nav> ); } export default Navbar;
useTheme() yang membungkus useContext(ThemeContext). Ini membuat kode kamu lebih clean dan mudah di-refactor.⚙️ useReducer: State Management Ala Mesin Kasir
Kalau useState cocok untuk state sederhana seperti toggle atau input tunggal, maka useReducer adalah solusinya ketika logika state kamu mulai kompleks — misalnya saat satu aksi mempengaruhi banyak nilai state sekaligus.
Bayangkan sebuah mesin kasir di toko. Kasir tidak langsung mengubah saldo secara asal — setiap transaksi ada tipe-nya (pembayaran, refund, diskon) dan prosesnya mengikuti aturan yang jelas. Itulah cara kerja useReducer: kamu kirim action ke reducer, dan reducer mengembalikan state baru berdasarkan aturan yang sudah kamu definisikan.
initialState: nilai awal state kamu
dispatch: fungsi untuk mengirim action ke reducer
import { useReducer } from 'react'; // 1. Definisikan reducer — otak dari state management function todoReducer(state, action) { switch (action.type) { case 'ADD_TODO': return [...state, { id: Date.now(), text: action.payload, done: false }]; case 'TOGGLE_TODO': return state.map(todo => todo.id === action.payload ? { ...todo, done: !todo.done } : todo ); case 'DELETE_TODO': return state.filter(todo => todo.id !== action.payload); default: return state; } } function TodoApp() { const [todos, dispatch] = useReducer(todoReducer, []); const [input, setInput] = useState(''); const handleAdd = () => { if (!input.trim()) return; dispatch({ type: 'ADD_TODO', payload: input }); setInput(''); }; return ( <div> <input value={input} onChange={e => setInput(e.target.value)} /> <button onClick={handleAdd}>Tambah</button> <ul> {todos.map(todo => ( <li key={todo.id}> <span onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })} style={{ textDecoration: todo.done ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => dispatch({ type: 'DELETE_TODO', payload: todo.id })}>❌</button> </li> ))} </ul> </div> ); }
🤝 Kombinasi Maut: useContext + useReducer = Global State Tanpa Redux
Nah, ini dia bagian paling keren! Kalau kamu menggabungkan useContext (untuk menyebarkan data global) dengan useReducer (untuk mengelola logika state yang kompleks), kamu mendapatkan arsitektur yang mirip Redux — tapi jauh lebih ringan dan tidak perlu library tambahan.
Pola ini sangat berguna untuk: autentikasi pengguna, keranjang belanja, pengaturan tema global, atau notifikasi aplikasi.
import { createContext, useContext, useReducer } from 'react'; // 1. Buat Context const CartContext = createContext(null); // 2. Reducer untuk logika keranjang function cartReducer(state, action) { switch (action.type) { case 'ADD_ITEM': const exists = state.find(i => i.id === action.payload.id); if (exists) return state.map(i => i.id === action.payload.id ? { ...i, qty: i.qty + 1 } : i ); return [...state, { ...action.payload, qty: 1 }]; case 'REMOVE_ITEM': return state.filter(i => i.id !== action.payload); case 'CLEAR_CART': return []; default: return state; } } // 3. Provider yang menggabungkan keduanya export function CartProvider({ children }) { const [cart, dispatch] = useReducer(cartReducer, []); return ( <CartContext.Provider value={{ cart, dispatch }}> {children} </CartContext.Provider> ); } // 4. Custom hook untuk konsumsi — best practice! export function useCart() { return useContext(CartContext); }
Dengan pola di atas, di komponen manapun kamu tinggal memanggil useCart() dan langsung dapat akses ke cart dan dispatch — tanpa prop drilling, tanpa library eksternal!
CartProvider di level paling atas yang dibutuhkan — jangan terlalu tinggi (global tanpa perlu) atau terlalu rendah (komponen tidak bisa akses). Prinsip ini disebut "lifting context to the right level".📊 Kapan Pakai useState vs useReducer vs Redux?
| Kriteria | useState | useContext + useReducer | Redux Toolkit |
|---|---|---|---|
| Kompleksitas State | Sederhana | Menengah–Kompleks | Sangat Kompleks |
| Sharing Antar Komponen | ❌ Prop Drilling | ✅ Global via Context | ✅ Global Store |
| Butuh Install Library? | ❌ Tidak | ❌ Tidak | ✅ Ya |
| DevTools Support | ⚡ Dasar | ⚡ Sedang | ✅ Sangat Lengkap |
| Rekomendasi Proyek | Komponen tunggal | Proyek menengah | Proyek besar / tim |
No comments:
Post a Comment