Penggunaan React useReducer



useReducer adalah hook di React untuk mengelola state yang kompleks dengan pola reducer (seperti Redux). Hook ini cocok digunakan ketika:
- State memiliki logika yang rumit dengan banyak sub-nilai.
- State berikutnya bergantung pada state sebelumnya.
- Anda ingin memisahkan logika state dari komponen untuk meningkatkan keterbacaan.
Berikut penjelasan lengkap dan contoh penggunaannya:
1. Sintaks Dasar
const [state, dispatch] = useReducer(reducer, initialArg, init?);
- reducer: Fungsi yang menentukan bagaimana state diperbarui.
- initialArg: Nilai awal state atau argumen untuk fungsi inisialisasi (init).
- init (opsional): Fungsi untuk inisialisasi state secara lazy.
2. Cara Kerja useReducer
a. Buat Reducer Function
Reducer menerima state saat ini dan action, lalu mengembalikan state baru.
function reducer(state, action) {
switch (action.type) {
case 'TAMBAH':
return { count: state.count + 1 };
case 'KURANGI':
return { count: state.count - 1 };
default:
return state;
}
}
b. Inisialisasi useReducer di Komponen
import { useReducer } from 'react';
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p>Hitungan: {state.count}</p>
<button onClick={() => dispatch({ type: 'TAMBAH' })}>+</button>
<button onClick={() => dispatch({ type: 'KURANGI' })}>-</button>
</div>
);
}
3. Contoh Lengkap: Todo List
a. Definisi Reducer dan State Awal
const initialState = {
todos: [],
input: ''
};
function todoReducer(state, action) {
switch (action.type) {
case 'SET_INPUT':
return { ...state, input: action.payload };
case 'TAMBAH_TODO':
return {
...state,
todos: [...state.todos, action.payload],
input: ''
};
case 'HAPUS_TODO':
return {
...state,
todos: state.todos.filter((_, index) => index !== action.payload)
};
default:
return state;
}
}
b. Komponen Todo
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, initialState);
const handleSubmit = (e) => {
e.preventDefault();
if (state.input.trim()) {
dispatch({ type: 'TAMBAH_TODO', payload: state.input });
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={state.input}
onChange={(e) =>
dispatch({ type: 'SET_INPUT', payload: e.target.value })
}
/>
<button type="submit">Tambah</button>
</form>
<ul>
{state.todos.map((todo, index) => (
<li key={index}>
{todo}
<button
onClick={() => dispatch({ type: 'HAPUS_TODO', payload: index })}
>
Hapus
</button>
</li>
))}
</ul>
</div>
);
}
4. Inisialisasi Lazy State
Jika inisialisasi state memerlukan komputasi berat, gunakan fungsi sebagai argumen ketiga:
function init(initialCount) {
return { count: initialCount };
}
function Counter({ initialCount }) {
const [state, dispatch] = useReducer(reducer, initialCount, init);
// ...
}
5. Perbandingan useReducer vs useState
Kriteria | useReducer | useState |
---|---|---|
Kompleksitas | Cocok untuk state kompleks | Cocok untuk state sederhana |
Logika Update | Terpusat di reducer | Tersebar di handler |
Testing | Mudah (reducer pure function) | Lebih sulit |
Boilerplate | Lebih banyak | Minimal |
6. Kapan Menggunakan useReducer?
- State memiliki banyak sub-nilai (contoh: form dengan banyak field).
- Transisi state kompleks (contoh: drag-and-drop, state machine).
- Logika state perlu digunakan di banyak komponen (kombinasikan dengan useContext).
- Untuk optimisasi performa (dispatch tidak berubah, cocok untuk optimisasi memoization).
7. Best Practices
-
Gunakan Aksi Terstandarisasi
Definisikan tipe aksi sebagai konstanta untuk menghindari typo:const ACTIONS = { TAMBAH_TODO: 'TAMBAH_TODO', HAPUS_TODO: 'HAPUS_TODO' };
-
Pisahkan Reducer dari Komponen
Simpan reducer di file terpisah agar mudah di-test dan di-reuse. -
Hindari Mutasi Langsung State
Selalu kembalikan state baru (gunakan spread operator atau Immer untuk objek/array):// ❌ Salah state.todos.push(action.payload); return state; // ✅ Benar return { ...state, todos: [...state.todos, action.payload] };
Contoh Lain: Form Kompleks
const formReducer = (state, action) => {
switch (action.type) {
case 'UPDATE_FIELD':
return {
...state,
[action.field]: action.value
};
case 'RESET':
return initialState;
default:
return state;
}
};
function UserForm() {
const [state, dispatch] = useReducer(formReducer, {
name: '',
email: '',
password: ''
});
const handleSubmit = (e) => {
e.preventDefault();
console.log(state);
dispatch({ type: 'RESET' });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={state.name}
onChange={(e) =>
dispatch({ type: 'UPDATE_FIELD', field: 'name', value: e.target.value })
}
/>
<input
type="email"
value={state.email}
onChange={(e) =>
dispatch({ type: 'UPDATE_FIELD', field: 'email', value: e.target.value })
}
/>
<button type="submit">Submit</button>
</form>
);
}
Kesimpulan
useReducer adalah solusi ideal untuk mengelola state yang kompleks dan terstruktur, sementara useState lebih cocok untuk state sederhana. Pilih sesuai kebutuhan aplikasi!