Latest

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.
[Arsitektur State Container useReducer - Part 7]
• 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 cartReducer tanpa 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