React: Membangun Antarmuka Pengguna · 7/9
Nilai turunan, memo, dan ref
Hitung alih-alih menyimpan, cache hanya setelah diukur, dan pakai ref hanya untuk kebutuhan darurat yang nyata.
Baca 28 menit
Setelah pelajaran ini kamu bisa
- Menghitung nilai saat render alih-alih menyimpannya
- Memakai `useMemo` hanya kalau ada pengukuran yang membenarkannya
- Menjelaskan apa itu ref dan bedanya dengan state
- Memilih dengan tepat antara state dan ref
Aturannya singkat: kalau bisa dihitung, jangan disimpan. Dua salinan informasi yang sama cepat atau lambat akan berbeda, dan setiap state tambahan adalah satu hal lagi yang harus dijaga tetap sejalan.
Disimpan versus diturunkan, diberi rangkaian pembaruan di mana satu jalur melewatkan sebuah langkah.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
// The React shape of the same idea.
function Cart({ items }) {
// Just calculate. This runs on every render, and that is fine.
const total = items.reduce((sum, i) => sum + i.price, 0);
const isEmpty = items.length === 0;
const expensive = items.filter((i) => i.price > 100);
return <p>{isEmpty ? "Empty" : total}</p>;
}useMemo, dan kapan tidak
useMemo men-cache sebuah perhitungan antar render. Ia untuk kasus jarang di mana sebuah perhitungan benar-benar mahal — dan ia punya biayanya sendiri, jadi memakainya di mana-mana membuat aplikasi lebih lambat, bukan lebih cepat.
// Not worth memoising. The comparison costs more than the work.
const total = useMemo(() => items.reduce((s, i) => s + i.price, 0), [items]);
const total = items.reduce((s, i) => s + i.price, 0); // just do this
// Worth memoising: measured, and genuinely slow.
const sorted = useMemo(
() => hugeList.slice().sort(expensiveComparator),
[hugeList],
);
// Also legitimate: keeping a reference stable for a dependency array,
// which is the object-dependency fix from the effects lesson.
const filters = useMemo(() => ({ query, page }), [query, page]);Ref
Ref adalah sebuah kotak yang bertahan melewati re-render tapi tidak memicunya saat berubah. Satu perbedaan itulah yang menentukan kapan kamu membutuhkannya.
| state | ref | |
|---|---|---|
| Bertahan melewati re-render | Ya | Ya |
| Mengubahnya memicu re-render | Ya | Tidak |
| Membaca nilai terbaru | Render berikutnya | Langsung, lewat .current |
| Dipakai untuk | Apa pun yang dilihat pengguna | Apa pun yang tidak dilihat pengguna |
import { useRef } from "react";
function SearchBox() {
// 1. Reaching a DOM element. The commonest legitimate use.
const inputRef = useRef<HTMLInputElement>(null);
function focusInput() {
inputRef.current?.focus(); // ?. because it is null before mount
}
// 2. Holding a value that must not cause a render.
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
function debouncedSearch(query: string) {
if (timerRef.current) clearTimeout(timerRef.current);
timerRef.current = setTimeout(() => search(query), 300);
}
return <input ref={inputRef} onChange={(e) => debouncedSearch(e.target.value)} />;
}ref adalah prop biasa, jadi function component bisa menerimanya langsung — forwardRef sudah tidak diperlukan.Akibat dari perbedaannya: ref menyimpan nilai terbaru, state menyimpan nilai render ini.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Tugas praktik
Di proyek ini, buka features/lesson/runner/code-runner.tsx dan daftar ketiga ref-nya plus keempat nilai state-nya. Untuk setiap ref, jelaskan dalam satu kalimat kenapa re-render akan salah atau tidak berguna. Lalu temukan satu nilai turunan yang dihitung saat render alih-alih disimpan — lineCount kandidat yang bagus.
Hasil yang diharapkan
Worker, timer, dan penanda running — tidak ada satu pun yang dilihat pengguna secara langsung.