Mempelajari Cara Menjinakkan React menggunakan Callback Hook
Diterbitkan: 2022-04-27Bukan rahasia lagi bahwa React.js telah menjadi sangat populer dalam beberapa tahun terakhir. Sekarang perpustakaan JavaScript pilihan bagi banyak pemain internet yang paling menonjol, termasuk Facebook dan WhatsApp.
Salah satu alasan utama kemunculannya adalah pengenalan kait di versi 16.8. Kait React memungkinkan Anda memanfaatkan fungsionalitas React tanpa menulis komponen kelas. Sekarang komponen fungsional dengan kait telah menjadi struktur masuk pengembang untuk bekerja dengan React.
Dalam posting blog ini, kita akan menggali lebih dalam ke satu kait khusus — useCallback
— karena menyentuh bagian mendasar dari pemrograman fungsional yang dikenal sebagai memoization. Anda akan tahu persis bagaimana dan kapan menggunakan hook useCallback
dan memanfaatkan kemampuan peningkatan kinerjanya sebaik mungkin.
Siap? Mari selami!
Apa itu Memoisasi?
Memoisasi adalah ketika fungsi kompleks menyimpan outputnya sehingga lain kali dipanggil dengan input yang sama. Ini mirip dengan caching, tetapi pada tingkat yang lebih lokal. Itu dapat melewati perhitungan rumit apa pun dan mengembalikan output lebih cepat karena sudah dihitung.
Ini dapat memiliki efek signifikan pada alokasi memori dan kinerja, dan ketegangan itulah yang dimaksudkan untuk dikurangi oleh kait useCallback
.
React useCallback vs useMemo
Pada titik ini, perlu disebutkan bahwa useCallback
berpasangan dengan baik dengan kait lain yang disebut useMemo
. Kita akan membahas keduanya, tetapi dalam bagian ini, kita akan fokus pada useCallback
sebagai topik utama.
Perbedaan utamanya adalah useMemo
mengembalikan nilai memo, sedangkan useCallback
mengembalikan fungsi memo. Itu berarti useMemo
digunakan untuk menyimpan nilai yang dihitung, sementara useCallback
mengembalikan fungsi yang dapat Anda panggil nanti.
Kait ini akan mengembalikan versi yang di-cache kecuali salah satu dependensinya (misalnya status atau properti) berubah.
Mari kita lihat dua fungsi dalam aksi:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
Cuplikan kode di atas adalah contoh yang dibuat-buat tetapi menunjukkan perbedaan antara dua panggilan balik:
-
memoizedValue
akan menjadi array[1, 2, 3, 4, 6, 9]
. Selama variabel values tetap ada, begitu jugamemoizedValue
, dan itu tidak akan pernah dihitung ulang. -
memoizedFunction
akan menjadi fungsi yang akan mengembalikan array[1, 2, 3, 4, 6, 9]
.
Apa yang hebat dari dua panggilan balik ini adalah mereka menjadi cache dan berkeliaran sampai array ketergantungan berubah. Ini berarti bahwa pada render, mereka tidak akan mengumpulkan sampah.
Rendering dan Bereaksi
Mengapa memoisasi penting dalam React?
Ini ada hubungannya dengan bagaimana React merender komponen Anda. React menggunakan DOM Virtual yang disimpan dalam memori untuk membandingkan data dan memutuskan apa yang akan diperbarui.
DOM virtual membantu Bereaksi dengan kinerja dan menjaga aplikasi Anda tetap cepat. Secara default, jika ada nilai dalam komponen Anda berubah, seluruh komponen akan dirender ulang. Ini membuat React "reaktif" terhadap input pengguna dan memungkinkan layar untuk memperbarui tanpa memuat ulang halaman.
Anda tidak ingin merender komponen Anda karena perubahan tidak akan memengaruhi komponen itu. Di sinilah memoisasi melalui useCallback
dan useMemo
berguna.
Saat React merender ulang komponen Anda, React juga membuat ulang fungsi yang telah Anda deklarasikan di dalam komponen Anda.
Perhatikan bahwa ketika membandingkan kesetaraan suatu fungsi dengan fungsi lain, mereka akan selalu salah. Karena fungsi juga merupakan objek, itu hanya akan sama dengan dirinya sendiri:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
Dengan kata lain, ketika React merender ulang komponen Anda, ia akan melihat semua fungsi yang dideklarasikan dalam komponen Anda sebagai fungsi baru.
Ini baik-baik saja sebagian besar waktu, dan fungsi sederhana mudah dihitung dan tidak akan memengaruhi kinerja. Tetapi di lain waktu ketika Anda tidak ingin fungsi tersebut dilihat sebagai fungsi baru, Anda dapat mengandalkan useCallback
untuk membantu Anda.
Anda mungkin berpikir, “Kapan saya tidak ingin suatu fungsi dilihat sebagai fungsi baru?” Nah, ada beberapa kasus ketika useCallback
lebih masuk akal:
- Anda meneruskan fungsi ke komponen lain yang juga di memo (
useMemo
) - Fungsi Anda memiliki keadaan internal yang perlu diingat
- Fungsi Anda adalah ketergantungan dari kait lain, seperti
useEffect
misalnya
Manfaat Kinerja dari React useCallback
Ketika useCallback
digunakan dengan tepat, ini dapat membantu mempercepat aplikasi Anda dan mencegah komponen dirender ulang jika tidak diperlukan.
Katakanlah, misalnya, Anda memiliki komponen yang mengambil data dalam jumlah besar dan bertanggung jawab untuk menampilkan data tersebut dalam bentuk bagan atau grafik, seperti ini:
Misalkan komponen induk untuk komponen visualisasi data Anda dirender ulang, tetapi properti atau status yang diubah tidak memengaruhi komponen tersebut. Dalam hal ini, Anda mungkin tidak ingin atau perlu merender ulang dan mengambil kembali semua data. Menghindari perenderan ulang dan pengambilan ulang ini dapat menghemat bandwidth pengguna Anda dan memberikan pengalaman pengguna yang lebih lancar.
Kekurangan React useCallback
Meskipun pengait ini dapat membantu Anda meningkatkan kinerja, pengait ini juga dilengkapi dengan jebakan. Beberapa hal yang perlu diperhatikan sebelum menggunakan useCallback
(dan useMemo
) adalah:
- Pengumpulan sampah: Fungsi lain yang belum di memo akan dibuang oleh React untuk mengosongkan memori.
- Alokasi memori: Mirip dengan pengumpulan sampah, semakin banyak fungsi memo yang Anda miliki, semakin banyak memori yang dibutuhkan. Plus, setiap kali Anda menggunakan callback ini, ada banyak kode di dalam React yang perlu menggunakan lebih banyak memori untuk memberi Anda output yang di-cache.
- Kompleksitas kode: Saat Anda mulai membungkus fungsi di kait ini, Anda segera meningkatkan kompleksitas kode Anda. Sekarang membutuhkan lebih banyak pemahaman tentang mengapa kait ini digunakan dan konfirmasi bahwa mereka digunakan dengan benar.
Menyadari perangkap di atas dapat menyelamatkan Anda dari sakit kepala karena tersandung sendiri. Saat mempertimbangkan untuk menggunakan useCallback
, pastikan manfaat kinerjanya lebih besar daripada kerugiannya.
React useCallback Contoh
Di bawah ini adalah pengaturan sederhana dengan komponen Button dan komponen Counter. Penghitung memiliki dua bagian status dan menampilkan dua komponen Tombol, masing-masing akan memperbarui bagian terpisah dari status komponen Penghitung.
Komponen Button memiliki dua props: handleClick
dan name. Setiap kali Tombol dirender, itu akan masuk ke konsol.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
Dalam contoh ini, setiap kali Anda mengklik salah satu tombol, Anda akan melihat ini di konsol:
// counter rendered // button1 rendered // button2 rendered
Sekarang, jika kita menerapkan useCallback
ke fungsi handleClick
kita dan membungkus Button kita di React.memo
, kita dapat melihat apa yang useCallback
kepada kita. React.memo
mirip dengan useMemo
dan memungkinkan kita untuk memoize sebuah komponen.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Sekarang ketika kita mengklik salah satu tombol, kita hanya akan melihat tombol yang kita klik untuk masuk ke konsol:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Kami telah menerapkan memoisasi ke komponen tombol kami, dan nilai prop yang diteruskan ke sana terlihat sama. Kedua fungsi handleClick
di-cache dan akan dilihat sebagai fungsi yang sama oleh React hingga nilai item dalam larik dependensi berubah (misalnya countOne
, countTwo
).
Ringkasan
useCallback
dan useMemo
, ingatlah bahwa mereka memiliki kasus penggunaan khusus — Anda tidak boleh membungkus setiap fungsi dengan kait ini. Jika fungsinya kompleks secara komputasi, ketergantungan dari hook atau prop lain yang diteruskan ke komponen memo adalah indikator bagus yang mungkin ingin Anda jangkau useCallback
.
Kami harap artikel ini membantu Anda memahami fungsionalitas React tingkat lanjut ini dan membantu Anda mendapatkan lebih banyak kepercayaan diri dengan pemrograman fungsional di sepanjang jalan!