Tutorial React Dasar Part 7: Menguasai useReducer untuk State Kompleks
Assalamualaikum Wr. Wb.
Tutorial React Dasar Part 7: Menguasai useReducer untuk State Kompleks | Kelola Logika Bisnis & Keranjang Belanja Lebih Terprediksi | Pada tahap awal belajar React, hook useState memang menjadi andalan utama kita untuk mengelola data komponen. Namun, bayangkan ketika Anda membangun fitur keranjang belanja (*Shopping Cart*), form pendaftaran dengan 15 field input validasi, atau sistem manajemen tugas (*To-Do List*). Jika kita membuat 8 sampai 10 baris useState yang saling berkaitan, kodingan akan menjadi sangat berantakan dan rawan terjadi *bug state* yang sulit dilacak. Di industri software modern, ketika state sudah melibatkan banyak aksi dan perubahan data bersarang (*nested state*), React menyediakan hook sakti bernama useReducer. Kali ini admin akan membagikan panduan step-by-step menguasai useReducer dengan pola Redux Pattern yang dijamin work 100%.
1. Kapan Harus Beralih dari useState ke useReducer?
Gunakan matriks perbandingan ini sebagai panduan praktis:
- Pilih useState: Jika tipe data state sederhana (string, boolean toggle ON/OFF, angka counter dasar) dan perubahannya berdiri sendiri.
- Pilih useReducer: Jika state berupa array/objek kompleks dengan banyak aksi berbeda (misal: Tambah Barang, Kurang Jumlah, Hapus Barang, Kosongkan Keranjang), atau ketika state berikutnya bergantung pada state sebelumnya.
• Reducer Function → Fungsi murni (pure function) penerima state saat ini & action objek
• Action → Objek pembawa instruksi tipe perubahan (type) dan data payload (payload)
• Dispatch → Fungsi pemicu untuk mengirimkan aksi dari UI ke fungsi Reducer
• Immutable State → Menjamin state lama tidak dimutasi langsung, melainkan mengembalikan salinan baru
Alur kerja pemrosesan state terpusat: UI memicu Dispatch → Reducer mengolah → State baru terupdate
2. Memahami 3 Elemen Kunci useReducer
Struktur penulisan useReducer terdiri dari tiga bagian utama:
// 1. Inisialisasi useReducer di komponen:
const [state, dispatch] = useReducer(reducerFunction, initialValue);
// 2. Format Action Object:
dispatch({ type: 'NAMA_AKSI', payload: data_tambahan });
3. Full Source Code: Fitur Keranjang Belanja (Shopping Cart)
Buka file src/App.jsx Anda dan coba implementasikan logika keranjang belanja lengkap berikut:
// File: src/App.jsx
import { useReducer } from 'react';
// 1. Nilai State Awal
const initialCart = [];
// 2. Reducer Function (Logika Bisnis Terpusat)
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
// Cek apakah produk sudah ada di keranjang
const itemAda = state.find((item) => item.id === action.payload.id);
if (itemAda) {
return state.map((item) =>
item.id === action.payload.id ? { ...item, qty: item.qty + 1 } : item
);
}
return [...state, { ...action.payload, qty: 1 }];
case 'REMOVE_ITEM':
return state.filter((item) => item.id !== action.payload.id);
case 'CLEAR_CART':
return [];
default:
return state;
}
}
function App() {
const [cart, dispatch] = useReducer(cartReducer, initialCart);
const daftarProduk = [
{ id: 1, nama: 'Sepatu Futsal Pro', harga: 450000 },
{ id: 2, nama: 'Jersey Original', harga: 220000 },
{ id: 3, nama: 'Kaos Kaki Grip', harga: 50000 }
];
// Hitung total belanjaan
const totalBiaya = cart.reduce((total, item) => total + item.harga * item.qty, 0);
return (
<div style={{ backgroundColor: '#0f172a', minHeight: '100vh', color: '#f8fafc', padding: '30px', fontFamily: 'Arial, sans-serif' }}>
<h1 style={{ color: '#38bdf8', textAlign: 'center' }}>🛒 Toko Olahraga (useReducer Demo)</h1>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '30px', maxWidth: '900px', margin: '30px auto' }}>
{/* Kolom 1: Daftar Produk */}
<div style={{ backgroundColor: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<h3 style={{ color: '#34d399' }}>Katalog Produk</h3>
{daftarProduk.map((prod) => (
<div key={prod.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px', paddingBottom: '8px', borderBottom: '1px solid #334155' }}>
<div>
<div style={{ fontWeight: 'bold' }}>{prod.nama}</div>
<div style={{ fontSize: '13px', color: '#94a3b8' }}>Rp {prod.harga.toLocaleString('id-ID')}</div>
</div>
<button
onClick={() => dispatch({ type: 'ADD_ITEM', payload: prod })}
style={{ padding: '6px 12px', backgroundColor: '#0284c7', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
>
+ Tambah
</button>
</div>
))}
</div>
{/* Kolom 2: Keranjang Belanja */}
<div style={{ backgroundColor: '#1e293b', padding: '20px', borderRadius: '8px', border: '1px solid #334155' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h3 style={{ color: '#f59e0b', margin: 0 }}>Isi Keranjang</h3>
{cart.length > 0 && (
<button
onClick={() => dispatch({ type: 'CLEAR_CART' })}
style={{ fontSize: '11px', padding: '4px 8px', backgroundColor: '#ef4444', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
>
Kosongkan
</button>
)}
</div>
{cart.length === 0 ? (
<p style={{ color: '#64748b', fontStyle: 'italic' }}>Keranjang belanja masih kosong.</p>
) : (
<>
{cart.map((item) => (
<div key={item.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
<span>{item.nama} <b style={{ color: '#38bdf8' }}>(x{item.qty})</b></span>
<div>
<span style={{ marginRight: '10px', fontSize: '13px' }}>Rp {(item.harga * item.qty).toLocaleString('id-ID')}</span>
<button
onClick={() => dispatch({ type: 'REMOVE_ITEM', payload: { id: item.id } })}
style={{ padding: '2px 6px', backgroundColor: '#475569', color: '#fca5a5', border: 'none', borderRadius: '3px', cursor: 'pointer' }}
>
✕
</button>
</div>
</div>
))}
<div style={{ marginTop: '20px', paddingTop: '12px', borderTop: '2px dashed #475569', display: 'flex', justifyContent: 'space-between', fontWeight: 'bold' }}>
<span>Total Pembayaran:</span>
<span style={{ color: '#34d399', fontSize: '16px' }}>Rp {totalBiaya.toLocaleString('id-ID')}</span>
</div>
</>
)}
</div>
</div>
</div>
);
}
export default App;
4. Keuntungan Arsitektur useReducer
- Logika Bebas dari Tampilan UI: Seluruh aturan bisnis (seperti penambahan kuantitas barang jika item sudah ada) berada rapi di dalam
cartReducertanpa mengotori kode JSX tampilan. - Mudah Di-unit Test: Fungsi reducer adalah fungsi murni (*pure function*) yang sangat mudah diuji karena input aksi yang sama akan selalu menghasilkan output state yang sama tanpa efek samping.
Itulah panduan menguasai hook useReducer di React. Pola ini adalah jembatan terbaik untuk memahami state management tingkat industri dan mempersiapkan Anda sebelum mempelajari library besar seperti Redux Toolkit atau Zustand.
Sekian tutorial Part 7 kali ini, silahkan share artikel ini jika Anda rasa bermanfaat untuk teman atau rekan yang sedang memperdalam arsitektur aplikasi React.
Semoga Bermanfaat ^_^
Terima Kasih.
Wassalamualaikum Wr. Wb.
Sumber gambar: Koleksi Pribadi / Analisis State Management useReducer React
0 Komentar
Posting Komentar
Pengunjung yang baik selalu meninggalkan komentar
Silahkan berikan komentar/kritik/saran tentang artikel ini untuk kemajuan blog ini,
Mohon untuk tidak berkomentar berbau SARA, Pornografi, Spam, dan kata-kata kasar agar tidak terjadi kesalah pahaman disini.
Terima Kasih