Belajar Redux - Debugging & Redux DevTools
Episode 19 of 23

Belajar Redux - Debugging & Redux DevTools

Episode ini membahas debugging Redux dengan Redux DevTools: time-travel debugging, action trace, state diff, dan jump-to-state, lalu cara memecahkan masalah umum seperti selector yang membuat objek baru, mutasi di luar reducer, stale state, dan error hydration.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Keunggulan paling dikenal Redux adalah debuggability: setiap perubahan state bisa ditelusuri, dibandingkan, dan diputar ulang. Episode 19 mengajarkan cara memanfaatkan Redux DevTools secara penuh, lalu menutup dengan troubleshooting masalah yang paling sering ditemui pengembang Redux di lapangan.

Redux Toolkit memasang Redux DevTools secara otomatis lewat configureStore. Alat ini bukan sekadar melihat state — dengan time-travel, action trace, dan state diff, kalian bisa menjawab tiga pertanyaan kunci saat debugging: kapan state berubah, apa yang menyebabkannya, dan apa saja yang ikut berubah.

Redux DevTools: Fitur Inti

Time-Travel Debugging

DevTools menyimpan seluruh histori action. Tombol jump memutar kembali state ke titik sebelum sebuah action dijalankan:

Aktifkan DevTools untuk Redux Toolkit
npm ls @reduxjs/toolkit react-redux

Langkah praktis:

  • Buka tab Redux di ekstensi DevTools browser.
  • Klik action di panel kiri untuk melihat state pada saat itu.
  • Gunakan tombol replay untuk menjalankan ulang urutan action dari awal.
  • Amati bagaimana UI bereaksi terhadap setiap langkah.

Time-travel sangat berguna untuk mereproduksi bug: kalian tidak perlu menebak urutan interaksi yang memicu error — cukup putar balik dan ulangi.

Action Trace dan State Diff

Tab Trace menampilkan stack trace asal action: fungsi mana yang memanggil dispatch. Tab Diff memperlihatkan perbedaan state sebelum dan sesudah action:

Contoh state diff
{
  "changeType": "UPDATE",
  "stateAfter": {
    "auth": { "user": { "id": 3, "name": "Budi" } },
    "posts": { "status": "succeeded", "items": [ { "id": 1 } ] }
  }
}

Diff membantu menemukan perubahan tak terduga — misalnya sebuah action seharusnya hanya mengubah satu field tetapi ternyata mengubah banyak field. Trace mempersempit pencarian ke kode yang melakukannya.

Jump to State

Tombol jump memungkinkan mengecek state pada titik mana pun tanpa harus mengulang interaksi. Ini cara tercepat untuk membuktikan hipotesis: "apakah bug sudah ada sejak state X?". Bandingkan state sehat dengan state bermasalah, lalu cari action yang terakhir mengubah field yang relevan.

Troubleshooting Umum

Selector Object Baru dan Infinite Render

Gejala: komponen merender terus-menerus atau React menolak render karena update loop. Penyebab paling umum: selector mengembalikan objek/array baru setiap kali dipanggil.

JSSelector bermasalah
const user = useAppSelector((state) => ({
  name: state.user.name,
  role: state.user.role,
}))

Setiap action membuat objek { name, role } baru, sehingga useSelector selalu mendeteksi perubahan. Perbaikan: gunakan shallowEqual atau buat selector termemoize dengan createSelector:

JSPerbaikan dengan shallowEqual
import { shallowEqual } from "react-redux"
 
const user = useAppSelector(
  (state) => ({
    name: state.user.name,
    role: state.user.role,
  }),
  shallowEqual,
)

Aturan sederhana: kembalikan nilai primitif dari selector, atau gunakan shallowEqual untuk objek/array yang dibaca sebagian.

Mutasi State di Luar Reducer

Redux Toolkit memakai Immer yang mendeteksi mutasi. Mutasi di luar reducer menghasilkan error:

Contoh pesan error Immer
Cannot produce a draft with an immutable value

Kesalahan umum: memodifikasi state yang didapat dari getState() di thunk, atau menyimpan referensi state ke variabel lalu memutasi di tempat lain. Perbaikan: semua perubahan hanya terjadi di dalam reducer (atau via updateQueryData untuk cache RTK Query):

JSPerbaikan: salin sebelum memutasi
const users = store.getState().users.items
const updated = users.map((u) =>
  u.id === 3 ? { ...u, role: "editor" } : u,
)
store.dispatch(usersLoaded(updated))

map menghasilkan array baru tanpa menyentuh state asli. Immer kemudian menangani pembaruan draft di dalam reducer.

Stale State dalam Callback

Masalah klasik: callback (seperti event handler atau setInterval) menangkap nilai state lama lewat closure:

JSClosure dengan state basi
const token = store.getState().auth.token
setTimeout(() => {
  fetch("/api/data", {
    headers: { Authorization: `Bearer ${token}` },
  })
}, 5000)

Jika login terjadi dalam 5 detik itu, token tetap nilai lama. Perbaikan: baca state langsung saat dibutuhkan dengan getState:

JSBaca state saat eksekusi
setTimeout(() => {
  const token = store.getState().auth.token
  fetch("/api/data", {
    headers: { Authorization: `Bearer ${token}` },
  })
}, 5000)

Pola ini juga berlaku untuk listener middleware — gunakan listenerApi.getState() daripada meng-capture nilai lebih awal.

Masalah Hydration di SSR

Error hydration muncul saat HTML server dan render client pertama tidak cocok. Sumber umum: tanggal atau random yang dihitung di render, dan state yang di-fetch dua kali dengan hasil berbeda:

JSMenghindari mismatch tanggal
export default function Clock() {
  const [now, setNow] = useState<string | null>(null)
  useEffect(() => setNow(new Date().toISOString()), [])
  if (!now) return null
  return <time>{now}</time>
}

Konten yang bergantung pada nilai runtime dipindah ke useEffect sehingga server merender placeholder yang sama dengan client. Untuk state yang harus sama persis, hidrasi dari server seperti di episode 14.

Tip

Aktifkan opsi trace di DevTools saat ingin menelusuri asal action. Di production, jangan aktifkan — melacak stack trace setiap action menambah overhead yang tidak perlu.

Penutup

Redux DevTools adalah superpower debugging: time-travel memutar ulang kejadian, action trace menemukan asal perubahan, dan state diff memperlihatkan cakupan perubahan. Dipadukan dengan pemahaman penyebab umum — selector yang membuat objek baru, mutasi di luar reducer, closure yang basi, dan hydration mismatch — kalian bisa mempersempit bug jauh lebih cepat daripada membaca log acak.

Inti yang harus dibawa pulang:

  • DevTools menyimpan histori action untuk time-travel debugging dan jump-to-state.
  • Action trace menunjukkan stack trace asal dispatch.
  • State diff memperlihatkan perubahan tak terduga antar action.
  • Selector yang mengembalikan objek baru menyebabkan re-render berlebihan; gunakan shallowEqual.
  • Immer menolak mutasi di luar reducer — salin state sebelum memutasi.
  • Baca state lewat getState saat dibutuhkan untuk menghindari closure basi.

Episode 20 berikutnya membahas fitur stabil terbaru RTK 2.x — kalian akan mengeksplorasi dukungan ESM dan TypeScript, Immer 10 dengan peningkatan performa, skills files untuk AI tooling, serta catatan rilis v2.11 hingga v2.12.0 tahun 2026.

Belajar Redux - Debugging & Redux DevTools | Belajar Redux