Mantap Jiwa! Ngelola State di React Pake Context API & useReducer: Panduan Lengkap Anti Ribet!

React State Management: Context API & useReducer Lengkap

PPLG

PPLG

Penulis

29 Aug 2026
61 x dilihat

Halo gaes, apa kabar nih para pejuang React JS? Pernah ngerasain kan gimana pusingnya kalau aplikasi React udah makin gede, terus tiba-tiba props drilling jadi momok menakutkan? Kamu harus nge-pass props dari atas ke bawah, ke bawah lagi, sampai ke komponen paling dalam cuma buat sepotong data. Beuh, PR banget, ngab!

Nah, di artikel ini, kita bakal spill jurus sakti buat ngatasin itu semua: Context API dan useReducer. Kombinasi dua hooks ini tuh powerful banget buat ngelola state yang kompleks di aplikasi React kamu biar lebih rapi, scalable, dan pastinya anti ribet! Skuy, langsung aja kita bedah!

Kenalan Dulu Sama Dua Bintang Tamu Kita!

Sebelum nyemplung ke kodenya, kita pahami dulu konsep dasar Context API dan useReducer secara santuy.

Context API: Solusi Anti Props Drilling

Bayangin Context API itu kayak papan buletin digital di kantor. Kamu bisa nulis pengumuman penting di situ (state) dan siapa aja yang ada di kantor (komponen) bisa baca pengumuman itu tanpa harus dikasih tahu satu per satu.

  • createContext: Ini buat bikin papan buletinnya. Di sinilah kita definisikan "slot" buat data yang mau dibagi.
  • Provider: Ini yang nempel di papan buletin. Dia yang "menyediakan" data ke semua komponen di bawahnya.
  • useContext: Ini kayak mata-mata yang baca papan buletin. Komponen yang butuh data tinggal panggil useContext dan boom! Data langsung nyampe tanpa harus dilempar-lempar lewat props.

Vibes-nya: Cocok banget buat data global yang diakses banyak komponen tapi jarang di-update, atau data yang memang spesifik buat sub-tree tertentu.

useReducer: Ketika useState Udah Nggak Cukup!

Pernah pake useState kan? Nah, useReducer ini bisa dibilang kakak tiri useState yang lebih jagoan buat ngurusin state yang kompleks. Kalau state kamu punya banyak transisi (bisa berubah dari A ke B, B ke C, A ke D) dan logic update-nya lumayan panjang, useReducer ini jawabannya.

  • reducer function: Ini inti dari useReducer. Dia fungsi murni yang nerima dua argumen: state saat ini dan action yang mau dilakuin. Tugasnya? Ngembaliin state yang baru berdasarkan action tadi. Ingat, pure function ya, gaes! Nggak boleh ada side effect di sini.
  • initialState: Ya, ini nilai awal dari state kita.
  • dispatch function: Ini fungsi yang kamu panggil buat "ngasih tahu" reducer kalau ada sesuatu yang harus diubah. Kamu bakal nge-dispatch sebuah objek action yang biasanya punya properti type (buat nandain aksi apa) dan payload (data yang dibawa aksi itu).

Vibes-nya: Cocok buat state yang kompleks, punya banyak related state (misal: isLoading, data, error), atau kalau kamu suka pola Redux tapi mau versi native React-nya.

The Dream Team: Context API + useReducer

Nah, kalau mereka berdua digabung, jadinya mantap banget! useReducer kita pake buat ngelola logic state yang kompleks (misal: nambahin item, ngapus item, toggle status). Sementara itu, Context API yang akan jadi kurir setia, ngebagi state terbaru dan fungsi dispatch ke semua komponen yang butuh, tanpa harus props drilling lagi. Ini bikin kode kamu lebih modular, gampang di-debug, dan performanya bisa lebih optimal kalau diatur dengan baik.

Hands-on: Bikin Aplikasi To-Do List Sederhana!

Skuy, langsung aja kita implementasiin di project React biar makin paham! Kita bakal bikin To-Do List sederhana.

Struktur File (Biar Rapi)

src/
├── components/
│   ├── AddTodoForm.jsx
│   ├── TodoItem.jsx
│   └── TodoList.jsx
├── context/
│   ├── TodoContext.jsx
│   └── todoReducer.js
├── App.jsx
└── main.jsx

Langkah 1: Siapin Reducer dan Initial State (src/context/todoReducer.js)

Pertama, kita definisiin reducer kita. Ini bakal jadi "otak" yang ngurusin semua perubahan state To-Do List.

// src/context/todoReducer.js

export const initialTodosState = [
  { id: 1, text: 'Belajar React Context API', completed: false },
  { id: 2, text: 'Nulis artikel ini', completed: true },
  { id: 3, text: 'Minum kopi', completed: false },
];

export const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: Date.now(), // ID unik sederhana
          text: action.payload,
          completed: false,
        },
      ];
    case 'TOGGLE_TODO':
      return state.map((todo) =>
        todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
      );
    case 'DELETE_TODO':
      return state.filter((todo) => todo.id !== action.payload);
    default:
      return state;
  }
};

Penjelasan:

  • initialTodosState: Ini data awal to-do kita.
  • todoReducer: Fungsi reducer yang nerima state dan action. Ada 3 tipe action yang kita support: ADD_TODO, TOGGLE_TODO, dan DELETE_TODO. Jangan lupa, reducer harus selalu mengembalikan state baru, jangan memodifikasi state lama secara langsung (mutating state) ya, gaes! Itu best practice-nya.

Langkah 2: Bikin Context dan Provider (src/context/TodoContext.jsx)

Ini adalah "jembatan" yang akan menghubungkan state dan dispatch ke seluruh komponen.

// src/context/TodoContext.jsx

import React, { createContext, useReducer } from 'react';
import { initialTodosState, todoReducer } from './todoReducer';

// 1. Bikin Context-nya
// Kita bikin dua Context: satu buat state, satu buat dispatch.
// Ini best practice biar komponen anak yang cuma butuh dispatch nggak ikutan re-render
// ketika state berubah, dan sebaliknya.
export const TodoStateContext = createContext(undefined);
export const TodoDispatchContext = createContext(undefined);

// 2. Bikin Provider Component-nya
export const TodoProvider = ({ children }) => {
  const [todos, dispatch] = useReducer(todoReducer, initialTodosState);

  return (
    <TodoStateContext.Provider value={todos}>
      <TodoDispatchContext.Provider value={dispatch}>
        {children}
      </TodoDispatchContext.Provider>
    </TodoStateContext.Provider>
  );
};

Penjelasan:

  • Kita bikin dua Context: TodoStateContext buat state todos dan TodoDispatchContext buat fungsi dispatch. Ini penting banget buat optimasi performa!
  • TodoProvider adalah komponen yang membungkus aplikasi kita. Di dalamnya, useReducer dipanggil dan todos (state) serta dispatch (fungsi untuk mengubah state) disediakan ke children-nya.

Langkah 3: Gabungin di App.jsx

Sekarang, kita pasang TodoProvider di level teratas aplikasi kita agar semua komponen di bawahnya bisa mengakses state dan dispatch.

// src/App.jsx

import React from 'react';
import { TodoProvider } from './context/TodoContext';
import AddTodoForm from './components/AddTodoForm';
import TodoList from './components/TodoList';

function App() {
  return (
    <TodoProvider>
      <div style={{ maxWidth: '600px', margin: '50px auto', padding: '20px', border: '1px solid #eee', borderRadius: '8px' }}>
        <h1>My To-Do List Gaes!</h1>
        <AddTodoForm />
        <TodoList />
      </div>
    </TodoProvider>
  );
}

export default App;

Penjelasan:

  • App kita sekarang dibungkus sama TodoProvider. Artinya, AddTodoForm dan TodoList (serta komponen di dalamnya) bisa langsung ngambil state dan dispatch dari context.

Langkah 4: Konsumsi State dan Dispatch di Komponen Anak

Sekarang giliran komponen AddTodoForm, TodoList, dan TodoItem ngambil data dan fungsi dispatch dari Context.

src/components/AddTodoForm.jsx

// src/components/AddTodoForm.jsx

import React, { useState, useContext } from 'react';
import { TodoDispatchContext } from '../context/TodoContext';

function AddTodoForm() {
  const [newTodoText, setNewTodoText] = useState('');
  const dispatch = useContext(TodoDispatchContext); // Ambil dispatch dari Context

  const handleSubmit = (e) => {
    e.preventDefault();
    if (newTodoText.trim() === '') return;

    dispatch({ type: 'ADD_TODO', payload: newTodoText }); // Dispatch action ADD_TODO
    setNewTodoText('');
  };

  return (
    <form onSubmit={handleSubmit} style={{ marginBottom: '20px' }}>
      <input
        type="text"
        value={newTodoText}
        onChange={(e) => setNewTodoText(e.target.value)}
        placeholder="Tambah to-do baru..."
        style={{ padding: '8px', marginRight: '10px', width: '70%' }}
      />
      <button type="submit" style={{ padding: '8px 15px', cursor: 'pointer' }}>
        Tambah
      </button>
    </form>
  );
}

export default AddTodoForm;

Penjelasan:

  • Kita cuma butuh dispatch di sini, jadi kita useContext(TodoDispatchContext).
  • Saat form di-submit, kita dispatch action ADD_TODO dengan newTodoText sebagai payload-nya.

src/components/TodoItem.jsx

// src/components/TodoItem.jsx

import React, { useContext } from 'react';
import { TodoDispatchContext } from '../context/TodoContext';

function TodoItem({ todo }) {
  const dispatch = useContext(TodoDispatchContext); // Ambil dispatch dari Context

  const handleToggle = () => {
    dispatch({ type: 'TOGGLE_TODO', payload: todo.id }); // Dispatch TOGGLE_TODO
  };

  const handleDelete = () => {
    dispatch({ type: 'DELETE_TODO', payload: todo.id }); // Dispatch DELETE_TODO
  };

  return (
    <li style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px', padding: '10px', border: '1px solid #f0f0f0', borderRadius: '4px', textDecoration: todo.completed ? 'line-through' : 'none' }}>
      <span onClick={handleToggle} style={{ cursor: 'pointer' }}>
        {todo.text}
      </span>
      <button onClick={handleDelete} style={{ background: 'salmon', color: 'white', border: 'none', padding: '5px 10px', borderRadius: '4px', cursor: 'pointer' }}>
        X
      </button>
    </li>
  );
}

export default TodoItem;

Penjelasan:

  • Sama kayak AddTodoForm, kita cuma butuh dispatch.
  • handleToggle dan handleDelete akan dispatch action yang sesuai.

src/components/TodoList.jsx

// src/components/TodoList.jsx

import React, { useContext } from 'react';
import { TodoStateContext } from '../context/TodoContext';
import TodoItem from './TodoItem';

function TodoList() {
  const todos = useContext(TodoStateContext); // Ambil state todos dari Context

  return (
    <ul style={{ listStyle: 'none', padding: 0 }}>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
}

export default TodoList;

Penjelasan:

  • Di sini, kita hanya butuh state todos, jadi kita useContext(TodoStateContext).
  • List to-do akan di-render berdasarkan data todos yang diambil dari Context.

Gimana, gaes? Simpel tapi powerful banget, kan? Nggak ada lagi tuh props drilling yang bikin mumet kepala!

Tips Praktis Biar Makin GG (Good Game)!

  1. Struktur Folder yang Rapi: Untuk project kecil, satu folder context cukup. Kalau udah gede, bisa pisahin jadi store/reducers, store/actions, store/contexts. Penting banget biar nggak gampang nyasar.
  2. Split Context (State dan Dispatch): Kita udah lakuin di contoh di atas! Ini best practice paling penting buat performa. Kenapa? Kalau kita gabungin state dan dispatch di satu context, setiap kali state berubah, semua komponen yang pake context itu bakal re-render, bahkan yang cuma butuh dispatch doang. Dengan dipisah, komponen yang cuma butuh dispatch nggak akan re-render kalau cuma state yang berubah, karena fungsi dispatch itu stabil (tidak berubah antar render).
  3. Memoisasi Provider (Opsional, tapi Bagus): Kalau value yang kamu kasih ke Provider itu objek atau array yang dibuat ulang setiap render, bisa jadi masalah performa. Kamu bisa bungkus Provider kamu pake useMemo atau useCallback buat value yang lebih kompleks, tapi untuk kasus kita tadi (state array dan dispatch function), React udah cukup pinter kok.
  4. Gunakan Konstan untuk Action Types: Daripada nulis type: 'ADD_TODO' terus-terusan, lebih baik definisikan konstan const ADD_TODO = 'ADD_TODO'; di satu file, terus diimpor. Ini biar nggak ada typo dan lebih gampang di-refactor.
  5. Perhatikan Kapan Harus Context, Kapan Props Biasa: Context API itu nggak selalu jadi solusi buat semua masalah. Kalau datanya cuma butuh di-pass ke satu atau dua level komponen di bawah, props biasa mungkin lebih simpel dan mudah dipahami. Jangan over-engineering ya!

Kesimpulan: Bye-bye Props Drilling!

Nah, itu dia gaes, panduan lengkap langkah demi langkah buat ngelola state di React pake kombinasi Context API dan useReducer. Dengan duo maut ini, kamu bisa bikin aplikasi React yang punya state management rapi, mudah di-maintain, dan performanya oke.

Nggak ada lagi alasan buat pusing sama props drilling atau state yang berantakan. Sekarang kamu udah punya tools yang super keren di tangan! Yuk, langsung dicoba di project kamu selanjutnya biar makin jago! Kalau ada pertanyaan, jangan sungkan spill di kolom komentar ya! Happy coding, ngab!


0.0

Berikan Rating

Komentar (0)

Silakan login untuk memberikan komentar.

Login Sekarang

Belum ada komentar. Jadilah yang pertama!

Menyukai Artikel (1)