Episode ini mengajarkan cara komponen membaca dan mengubah state: useSelector untuk membaca, useDispatch untuk mengirim action. Kalian memahami selector sebagai fungsi murni, kapan re-render terjadi dengan strict equality, serta memakai shallowEqual untuk data kompleks.

Store dan slice sudah ada, tapi belum berhubungan dengan UI. Episode 5 menjembatani keduanya: hooks dari react-redux yang menghubungkan komponen React dengan store Redux. Inilah momen di mana state yang selama ini hanya ada di dalam store akhirnya terlihat di layar.
Kita akan membahas useSelector untuk membaca state, useDispatch untuk mengirim action, prinsip selector sebagai fungsi murni, serta kapan dan bagaimana re-render terjadi — termasuk penggunaan shallowEqual untuk mencegah re-render berlebihan.
useSelector(selector) menerima fungsi selector dan mengembalikan bagian state yang dipilih. React Redux secara otomatis berlangganan ke store dan me-render ulang komponen saat hasil selector berubah.
import { useSelector, useDispatch } from "react-redux"
function Counter() {
const count = useSelector((state) => state.counter.count)
const dispatch = useDispatch()
return (
<div>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
)
}useSelector((state) => state.counter.count) membaca nilai count dari store. useDispatch() mengembalikan fungsi dispatch siap pakai, dan dispatch(increment()) mengirim action dari slice counter.
Sebuah selector adalah fungsi murni: menerima seluruh state dan mengembalikan data yang dipilih. Karena murni, selector bisa diuji, ditulis ulang, dan digabungkan dengan mudah.
const selectCount = (state) => state.counter.countMenulis selector sebagai konstanta terpisah memungkinkan pemakaian ulang di banyak komponen dan menjadi dasar memoization createSelector yang akan kita bahas di episode 13.
React Redux membandingkan hasil selector dengan hasil sebelumnya menggunakan strict equality. Komponen hanya re-render jika nilainya berubah:
const name = useSelector((state) => state.user.name)state.user.name adalah string — primitive. String dibandingkan berdasarkan nilai, jadi re-render hanya terjadi saat nama benar-benar berubah.
Masalah muncul saat selector mengembalikan objek atau array yang dibuat baru setiap kali:
const user = useSelector((state) => ({
name: state.user.name,
age: state.user.age,
})){ name, age } adalah objek baru pada setiap evaluasi selector, sehingga hasilnya selalu berbeda menurut strict equality — komponen re-render terus-menerus. Solusinya di episode 13, tapi ada jalan pintas: shallowEqual di bawah.
Semakin kecil data yang dipilih, semakin jarang re-render terjadi. Pilih hanya field yang benar-benar dipakai komponen:
const total = useSelector((state) => state.cart.items.reduce((s, i) => s + i.qty, 0))Pola ini memang memicu re-render pada setiap perubahan isi keranjang, tapi hanya mengembalikan angka tunggal sehingga perbandingan state antar komponen tetap murah.
Ketika komponen butuh beberapa field sekaligus, gabungkan dengan shallowEqual:
import { useSelector, shallowEqual } from "react-redux"
const { name, age } = useSelector(
(state) => ({
name: state.user.name,
age: state.user.age,
}),
shallowEqual,
)shallowEqual membandingkan setiap field level atas secara individual, bukan referensi objeknya. Jika name dan age sama dengan sebelumnya, tidak ada re-render — solusi langsung untuk masalah objek baru di atas.
Untuk banyak nilai, satu panggilan useSelector boleh memilih beberapa field sekaligus asalkan digabung dengan comparator yang tepat:
const status = useSelector((state) => state.posts.status)
const items = useSelector((state) => state.posts.items)Dua panggilan selector terpisah juga sah dan justru lebih mudah di-predict. Gunakan panggilan terpisah untuk nilai yang jarang berubah, dan shallowEqual bila harus digabung dalam satu objek.
Episode 5 menutup lingkaran state management dasar: komponen membaca state dengan useSelector, mengubahnya dengan useDispatch, dan re-render terjadi hanya saat hasil selector benar-benar berubah. Performa sudah mulai menjadi perhatian — dan itu akan menjadi tema besar di episode 13.
Inti yang harus dibawa pulang:
useSelector(selector) membaca state dan berlangganan perubahan.useDispatch() mengembalikan fungsi untuk mengirim action.shallowEqual menyatukan beberapa field tanpa re-render berlebihan.Di episode 6 selanjutnya kalian akan melangkah ke dunia async: createAsyncThunk — membuat action yang mengambil data API, memahami lifecycle pending, fulfilled, dan rejected, mengelola status loading, succeeded, dan failed, serta membatalkan request saat komponen unmount.