Menu

React.memo: Lewati Render Ulang Saat Props Sama

memo membungkus sebuah komponen agar React melewatkan render ulangnya saat props-nya sama secara dangkal dengan sebelumnya. Pelajari kapan memo membantu, kenapa objek dan fungsi baru di props menggagalkannya, dan apa yang tidak dicegahnya.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

memo memungkinkan sebuah komponen melewatkan render ulang saat props-nya sama dengan sebelumnya. Bungkus komponen dengan memo(...), dan saat induknya merender, React lebih dulu membandingkan setiap prop baru dengan yang lama; jika semuanya sama, React memakai ulang hasil sebelumnya alih-alih memanggil komponen itu lagi.

Ketik di input-nya: input tetap responsif, dan Konsol hanya menampilkan render SlowList sekali, dari render pertama. Hapus wrapper memo( (beserta tanda kurung penutupnya) lalu ketik lagi: setiap ketukan tombol sekarang menunggu 150 ms untuk list-nya dan mencatat sebuah render.

Sintaksnya

import { memo } from 'react';

const MemoizedComponent = memo(SomeComponent, arePropsEqual?);
  • SomeComponent adalah function component apa pun. Gaya yang umum adalah langsung membungkus definisinya: const Card = memo(function Card(props) { ... }). Memberi nama pada fungsi di dalamnya menjaga nama itu tetap muncul di React DevTools dan di pesan error.
  • arePropsEqual bersifat opsional dan jarang dibutuhkan (lihat di bawah).
  • Hasilnya adalah komponen yang kamu render seperti komponen lain.

Secara default, React membandingkan props lama dan baru secara dangkal: setiap prop diperiksa dengan Object.is. String, angka, dan boolean dibandingkan berdasarkan nilai, jadi title="Hi" sama dengan title="Hi". Objek, array, dan fungsi dibandingkan berdasarkan referensi: dua objek dengan isi yang sama tetaplah objek yang berbeda.

Di contoh di atas, items dideklarasikan di luar komponen, jadi array-nya sama di setiap render, dan perbandingannya lolos.

Objek dan fungsi baru menggagalkan memo

Apa pun yang ditulis sebagai literal di dalam isi komponen induk dibuat ulang di setiap render: { name: 'Ada' }, [1, 2, 3], () => save(). Kirim salah satunya ke komponen memo, dan prop itu tidak pernah sama dengan yang sebelumnya.

Setiap klik mencatat render Badge object prop, tetapi tidak pernah versi string-nya. Ada tiga cara memperbaiki kasus objek:

  • Kirim nilai primitif. name="Ada", bukan user={{ name: 'Ada' }}, saat komponen anak hanya butuh satu atau dua field.
  • Pindahkan konstanta ke luar komponen. Objek yang tidak pernah berubah bisa tinggal di level modul, seperti items di contoh pertama.
  • Simpan di cache. Bungkus objek dan array yang dibuat dari props atau state dengan useMemo, dan fungsi dengan useCallback.

children punya masalah yang sama. <Card><p>Hello</p></Card> membuat objek elemen <p> baru di setiap render, jadi Card yang memakai memo dan menerima children akan dirender ulang setiap kali.

Yang tidak dicegah memo

memo hanya melihat props yang datang dari induk. Komponen yang memakai memo tetap merender saat:

  • state miliknya sendiri berubah, lewat useState atau useReducer miliknya;
  • context yang dibacanya berubah, lewat useContext.

Klik Unrelated, dan Panel dilewati. Ganti tema, dan komponen itu merender, karena ia membaca context. Klik Like, dan ia merender lagi, karena state miliknya sendiri berubah. Itulah perilaku yang dimaksudkan: memo adalah tentang melewatkan pekerjaan saat tidak ada yang berubah dari hal-hal yang menjadi dependensi komponen, dan context serta state termasuk dependensinya.

Fungsi perbandingan kustom

Argumen kedua menggantikan perbandingan dangkal. Fungsi ini menerima props sebelumnya dan berikutnya, lalu mengembalikan true saat keduanya harus dianggap sama, yang berarti React melewatkan render.

const MemoChart = memo(Chart, (prev, next) => {
    return (
        prev.points.length === next.points.length &&
        prev.points.every((p, i) => p === next.points[i]) &&
        prev.color === next.color &&
        prev.onSelect === next.onSelect
    );
});

Kamu jarang membutuhkan ini, dan mudah sekali salah. Jika fungsinya mengabaikan sebuah prop, komponen itu terus menampilkan nilai lama prop tersebut. Mengabaikan prop berupa fungsi adalah kasus terburuk: komponen yang dilewati terus memanggil fungsi lama, yang membaca state lama. Bandingkan setiap prop, dan jangan lakukan pemeriksaan kesamaan mendalam pada data besar, karena perbandingannya jadi lebih mahal daripada render yang dihematnya. Props yang stabil lewat useMemo dan useCallback hampir selalu menjadi perbaikan yang lebih baik.

Memeriksa apakah memo berfungsi

Cara memeriksa yang paling cepat adalah yang dipakai di halaman ini: console.log di bagian atas komponen. Jika log tercetak saat kamu mengharapkan render dilewati, catat juga props-nya, lalu bandingkan dengan yang sebelumnya untuk menemukan prop yang berubah. Di proyek sungguhan, React DevTools membantu lebih jauh: nyalakan "Highlight updates when components render" di pengaturannya untuk melihat komponen mana yang berkedip di setiap interaksi, dan tab Profiler merekam setiap render dan, dengan pengaturan yang sesuai dinyalakan, alasan setiap komponen merender.

Ada dua hal yang terlihat seperti memo gagal padahal tidak:

  • Render pertama selalu terjadi. memo hanya melewatkan render setelah komponen dirender sekali dengan props tersebut.
  • Build development bisa merender lebih banyak. Di bawah StrictMode, React merender setiap komponen dua kali saat development untuk menangkap kode yang tidak murni. Pratinjau di sini berjalan seperti build production, jadi setiap baris log di atas berarti satu render sungguhan.

Kapan memakai memo

memo layak dipakai saat sebuah komponen lambat dirender dan induknya sering merender dengan props yang sama untuknya: list panjang atau grafik di samping input, sidebar di dalam halaman yang diperbarui setiap detik. Untuk komponen yang cepat dirender, atau yang props-nya memang berubah setiap kali, memo hanya menambah perbandingan tanpa memberi manfaat. Sebelum melakukan memoization, lihat apakah kamu bisa mengubah strukturnya, dengan memindahkan state ke bawah atau mengirim JSX sebagai children, seperti yang ditunjukkan di halaman rendering.

React Compiler 1.0, jika proyekmu memakainya, melakukan memoization pada komponen dan props-nya secara otomatis saat build, sehingga sebagian besar pemanggilan memo manual tidak lagi diperlukan. Tanpa React Compiler, memo bekerja persis seperti yang dijelaskan di sini dan tetap umum di kode React.

Pertanyaan yang sering diajukan

Apa fungsi React.memo?

React.memo mengembalikan versi komponen yang sudah memakai memoization. Saat induk merender, React membandingkan props baru dengan props sebelumnya dan melewatkan render komponen itu jika setiap prop sama menurut Object.is.

Kenapa komponen memo saya tetap dirender ulang?

Biasanya karena sebuah prop adalah objek, array, atau fungsi baru yang dibuat saat induk merender, jadi tidak pernah sama dengan yang sebelumnya. Komponen itu juga merender saat state miliknya sendiri berubah atau saat context yang dibacanya berubah, dengan atau tanpa memo.

Apa perbedaan React.memo dan useMemo?

memo membungkus komponen dan melewatkan render-nya. useMemo adalah hook di dalam komponen yang menyimpan nilai hasil perhitungan. Keduanya sering bekerja bersama: useMemo menjaga prop berupa objek tetap stabil agar memo bisa melewatkan render.

Apakah sebaiknya setiap komponen saya bungkus dengan memo?

Tidak. Sebagian besar render itu murah, dan perbandingan props punya biayanya sendiri. Pakai untuk komponen yang lambat dirender dan sering menerima props yang sama.

Apakah memo berfungsi dengan children?

Jarang. JSX yang dikirim sebagai children adalah objek baru di setiap render, jadi komponen memo yang menerima children hampir selalu dirender ulang.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai