- Bab 7 dari 10
- 35 menit
- 1 latihan kode
- 4 soal kuis
useRef, useReducer, memoisasi, dan custom hooks
Di bab ini (8 bagian)
Tujuan bab ini
- Memakai
useRefuntuk mengakses elemen DOM dan menyimpan nilai yang tidak memicu render. - Mengelola state dengan banyak aksi lewat
useReducerdan reducer yang murni. - Tahu kapan
useMemo,useCallback, danmemoberguna, dan kapan justru tidak perlu. - Membuat custom hook sendiri dan mematuhi aturan hooks.
useRef: kotak yang diingat tanpa render ulang
useRef(nilaiAwal) mengembalikan object { current: nilaiAwal } yang sama persis di setiap render. Kamu boleh mengubah ref.current kapan saja, dan React tidak merender ulang karenanya. Analoginya laci meja: kamu bisa menyimpan dan mengganti isinya, tapi tidak ada yang mengumumkan perubahan itu ke seluruh kelas.
Pemakaian pertama: mengakses elemen DOM, misalnya memfokuskan input.
Saat render pertama inputRef.current masih null. Setelah React memasang elemen ke DOM, current diisi elemen <input>, jadi aksesnya dilakukan di efek atau event handler, bukan saat render.
Pemakaian kedua: menyimpan nilai antar render yang tidak perlu tampil, misalnya ID timer.
Kenapa ID timer tidak disimpan di state? Karena tidak ditampilkan, dan menyimpannya di state hanya membuat render tambahan yang sia-sia. Sebaliknya, kalau sebuah nilai ditampilkan, simpan di state, karena perubahan ref.current tidak akan memperbarui layar.
useReducer: semua aturan perubahan di satu tempat
Kalau satu state punya banyak cara berubah (tambah, kurang, reset, set ke angka tertentu), handler-mu bisa berisi logika yang tersebar di banyak tempat. useReducer mengumpulkannya di satu fungsi reducer.
Istilahnya:
- Action: object yang menjelaskan apa yang terjadi, biasanya
{ type: "tambah" }, kadang dengan data tambahan seperti{ type: "hapus", id: 3 }. - dispatch: fungsi untuk mengirim action ke reducer.
- Reducer: fungsi
(state, action) => stateBaru. Wajib murni: tidak mengubahstatelama, tidak fetch, tidakMath.random(), cukup menghitung state baru dan mengembalikannya.
Bonusnya, reducer adalah fungsi JavaScript biasa, jadi gampang diuji tanpa React: reducer({ n: 1 }, { type: "tambah" }) harus menghasilkan { n: 2 }. Itulah yang akan kamu latih di latihan bab ini.
| Pilih | Kalau |
|---|---|
useState | Data tampilan sederhana, satu atau dua cara berubah |
useReducer | Satu data punya banyak aksi yang saling berkaitan, logika percabangan mulai berulang |
useRef | Nilai perlu diingat tapi tidak ditampilkan, atau butuh akses DOM |
Panduan praktis: mulai dengan useState, pindah ke useReducer saat handler mulai berantakan.
useMemo, useCallback, dan memo
Ingat, setiap render memanggil ulang fungsi komponen, jadi semua perhitungan dan fungsi di dalamnya dibuat ulang. Biasanya ini tidak masalah, JavaScript cepat. Tiga alat ini untuk kasus khusus.
useMemo menyimpan hasil perhitungan dan hanya menghitung ulang bila dependency berubah.
memo membungkus komponen supaya tidak dirender ulang bila props-nya sama (dibandingkan dangkal dengan Object.is).
useCallback menyimpan referensi fungsi supaya tetap sama antar render. Ini penting justru karena memo: fungsi baru di setiap render berarti props baru, dan memo jadi tidak berguna.
Kapan perlu? Saat perhitungannya benar-benar berat (menyaring ribuan data), saat fungsi atau object dioper ke komponen yang dibungkus memo, atau saat nilainya jadi dependency efek. Kapan tidak perlu? Untuk perhitungan ringan seperti a + b, atau dipasang di semua variabel "biar aman". Memoisasi juga punya biaya (menyimpan nilai dan membandingkan dependency) dan membuat kode lebih sulit dibaca. Prinsipnya: ukur dulu dengan React DevTools Profiler, baru optimasi. Kabar baiknya, React Compiler (versi stabil 1.0 sejak Oktober 2025) bisa melakukan memoisasi otomatis, jadi kebutuhan menulis ini manual makin sedikit.
Custom hooks: logika yang bisa dipakai ulang
Custom hook adalah fungsi biasa yang namanya diawali use dan memanggil hook lain. Ia membagikan logika, bukan state: setiap komponen yang memanggilnya punya state sendiri.
Contoh lain, menyimpan state ke localStorage supaya tidak hilang saat halaman di-refresh:
Perhatikan useState(() => ...): fungsi inisialisasi ini hanya jalan sekali saat mount, jadi localStorage tidak dibaca di setiap render.
Aturan hooks
React mengenali setiap state berdasarkan urutan pemanggilan hook. Hook pertama selalu state nomor 1, hook kedua state nomor 2, dan seterusnya. Kalau urutannya berubah antar render, state bisa tertukar. Maka ada dua aturan wajib:
- Panggil hook hanya di level atas: jangan di dalam
if,for,while, fungsi bersarang, atau setelahreturnlebih awal. - Panggil hook hanya dari function component atau custom hook, bukan dari fungsi JavaScript biasa atau class component.
Plugin eslint-plugin-react-hooks (aturan rules-of-hooks) menangkap pelanggaran ini otomatis.
Kesalahan umum
- Menampilkan
ref.currentdan heran layar tidak berubah. Nilai yang tampil harus di state. - Membaca
ref.currentelemen saat render: nilainya masihnull. Akses di efek atau handler. - Reducer yang memutasi state (
state.n++; return state;): referensinya sama, React bisa tidak merender ulang. - Lupa
default: return statedi reducer, sehingga action tak dikenal menghasilkanundefined. - useMemo dan useCallback di mana-mana tanpa masalah performa yang terbukti.
- Custom hook tanpa awalan
use: linter tidak bisa memeriksa aturan hooks di dalamnya.
Rangkuman
useRefmenyimpan nilai diref.currenttanpa memicu render, dipakai untuk akses DOM dan nilai seperti ID timer.useReducermengumpulkan semua perubahan state di reducer murni(state, action) => stateBaru, dipicu lewatdispatch.useMemomenyimpan hasil hitungan,useCallbackmenyimpan fungsi,memomelewati render bila props sama. Pakai setelah mengukur.- Custom hook adalah fungsi berawalan
useyang memanggil hook lain untuk berbagi logika, sepertiuseFetchdanuseLocalStorage. - Hook hanya dipanggil di level atas function component atau custom hook, dengan urutan yang sama di setiap render.
Slide 25 sampai 28 dari deck ReactJS. Penjelasan lengkapnya ada di mode Baca.

Selesaikan bab ini
- 1. Baca materi
- 2. Latihan kode (1)
- 3. Kuis (4 soal)
Latihan kode: Reducer keranjang belanja
Komponen keranjang memakai const [state, dispatch] = useReducer(keranjangReducer, { items: [] }), dan tombolnya memanggil misalnya dispatch({ type: "tambah", produk: { id: 1, nama: "Kaos", harga: 80000 } }).
Lengkapi keranjangReducer(state, action) sebagai fungsi murni (jangan ubah state lama):
"tambah": bila produk denganiditu belum ada, tambahkan{ ...produk, jumlah: 1 }; bila sudah ada, naikkanjumlah-nya 1."hapus": buang item denganaction.id."ubahJumlah": setjumlahitemaction.idmenjadiaction.jumlah; bilaaction.jumlah0 atau kurang, buang item itu."kosongkan":itemsmenjadi array kosong.- Action lain: kembalikan
stateyang sama persis.
Lengkapi juga totalHarga(state) (dipakai di JSX lewat useMemo) yang menjumlahkan harga * jumlah.
Cek pemahaman
1Hook mana yang paling tepat untuk menyimpan ID dari setInterval yang tidak perlu ditampilkan?
2Apa yang ditampilkan kode berikut?
function reducer(state, action) { switch (action.type) { case "tambah": return { n: state.n + 1 }; case "kali": return { n: state.n * action.by }; default: return state; } } let s = { n: 0 }; s = reducer(s, { type: "tambah" }); s = reducer(s, { type: "tambah" }); s = reducer(s, { type: "kali", by: 3 }); s = reducer(s, { type: "aneh" }); console.log(s.n);3Dua komponen yang sama-sama memanggil useLocalStorage("tema", "terang") otomatis berbagi satu state React yang sama.
4Kapan useCallback benar-benar berguna?