Lompat ke konten
Kembali ke modul

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.

Coba sendiri

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.

tsx
// 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>;
}
Tiga nilai turunan, tanpa state, tanpa effect, dan tidak ada kemungkinan mereka jadi usang.

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.

tsx
// 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.

stateref
Bertahan melewati re-renderYaYa
Mengubahnya memicu re-renderYaTidak
Membaca nilai terbaruRender berikutnyaLangsung, lewat .current
Dipakai untukApa pun yang dilihat penggunaApa pun yang tidak dilihat pengguna
tsx
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)} />;
}
Di React 19 ref adalah prop biasa, jadi function component bisa menerimanya langsung — forwardRef sudah tidak diperlukan.
Coba sendiri

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.