Dalam artikel ini, kita akan belajar tentang useCallback di React untuk meningkatkan performa aplikasi. Dapatkan tips dan contoh praktis untuk mengoptimalkan re-render dan kode efisien.
useCallback
dalam React adalah hook yang bertujuan untuk mengoptimalkan kinerja dengan mengurangi jumlah re-render yang tidak perlu pada komponen lain di dalamnya. Pada dasarnya, dalam aplikasi React, setiap kali sebuah komponen di-render, semua fungsi di dalam komponen tersebut juga dibuat ulang. Ini berarti, meskipun isi dari fungsi tersebut tetap sama antara satu render ke render berikutnya, React menganggapnya sebagai fungsi baru pada setiap render.
Ketika fungsi ini diteruskan ke komponen lain yang ada di dalamnya, komponen tersebut bisa mengalami re-render yang tidak perlu karena dari sudut pandangnya, ia menerima prop yang 'baru' pada setiap render komponen induk, meski secara fungsional fungsi tersebut tidak berubah.
useCallback
membantu dalam situasi ini dengan memungkinkan kita untuk 'mengingat' fungsi yang sama di antara render. Ini dicapai dengan memberikan fungsi tersebut pada useCallback
bersama dengan daftar dependensi. Jadi, fungsi yang dibungkus dengan useCallback
hanya akan dibuat ulang jika ada perubahan pada nilai di dalam daftar dependensinya.
Berikut adalah contoh penggunaanya:
const memoizedFunction = useCallback(() => {
//
}, [dependencies]);
Di sini, memoizedFunction
akan mempertahankan identitasnya yang sama selama nilai dalam dependencies
tidak berubah.
Penggunaan useCallback
efektif dalam kasus di mana re-render yang tidak perlu pada komponen lain bisa mempengaruhi performa, atau ketika dibutuhkan referensi yang stabil untuk fungsi tersebut antar render. Namun, penggunaannya harus bijaksana karena kadang-kadang overhead dari proses memoization bisa lebih berat daripada manfaat yang diperoleh, terutama jika fungsi tersebut tidak sering berubah atau tidak berdampak signifikan terhadap performa komponen.
useCallback
?useCallback
sebaiknya digunakan dalam kondisi-kondisi tertentu di mana optimasi performa menjadi penting. Salah satu skenario utama adalah ketika fungsi yang didefinisikan di dalam sebuah komponen perlu di-pass sebagai props ke komponen lain yang ada di dalamnya. Terutama jika komponen tersebut dioptimalkan dengan React.memo
, penggunaan useCallback
akan mencegah komponen anak tersebut dari re-render yang tidak perlu karena fungsi yang diterima sebagai props akan mempertahankan referensinya yang sama di antara render, kecuali jika dependensi yang ditentukan berubah.
Situasi lain di mana useCallback
menjadi berguna adalah ketika sebuah fungsi digunakan sebagai dependensi di useEffect
atau hook lainnya. Tanpa useCallback
, fungsi tersebut akan dibuat ulang pada setiap render, memicu kembali pemanggilan useEffect
atau hook lainnya yang tidak perlu. Ini berguna untuk menghindari efek samping yang tidak diinginkan dan menjaga logika aplikasi tetap konsisten.
Pada dasarnya, useCallback
membantu dalam menjaga stabilitas referensi fungsi di antara render, yang penting dalam kasus di mana pembuatan ulang fungsi yang sering dapat mempengaruhi performa atau perilaku komponen.
Menghindari pembuatan ulang fungsi yang tidak perlu dalam React, seperti yang dilakukan melalui useCallback
, penting karena beberapa alasan:
Performa: Di aplikasi React yang besar dan kompleks, setiap bit performa sangat berharga. Fungsi yang dibuat ulang pada setiap render, terutama jika diteruskan ke komponen lain, dapat menyebabkan re-render yang tidak perlu pada komponen tersebut. Ini memperberat proses pembaruan DOM virtual dan dapat mengurangi kecepatan respons aplikasi, terutama pada aplikasi dengan banyak komponen interaktif.
Penghematan Sumber Daya: Pembuatan ulang fungsi yang tidak perlu memakai sumber daya komputasi. Meskipun pembuatan ulang fungsi sederhana mungkin tidak berdampak signifikan, fungsi yang lebih kompleks atau pembuatan ulang yang terjadi di banyak komponen sekaligus bisa membebani sistem.
Efek Samping dalam Komponen Lain: Saat fungsi yang dibuat ulang diteruskan ke komponen lain sebagai props, dan jika komponen tersebut menggunakan React.memo
atau memiliki logika yang bergantung pada referensi yang stabil, pembuatan ulang fungsi dapat memicu re-render yang tidak diinginkan. Ini bisa menyebabkan perilaku yang tidak konsisten atau efek samping yang sulit di-debug.
Konsistensi Referensi: Dalam React, konsistensi referensi sangat penting, terutama saat bekerja dengan hook seperti useEffect
dan useMemo
. Jika fungsi yang menjadi dependensi di dalam hook tersebut berubah identitasnya pada setiap render, maka akan memicu pemanggilan kembali hook tersebut, yang bisa mengakibatkan perilaku tak terduga atau komputasi yang berlebihan.
Pengalaman Pengguna: Akhirnya, semua poin di atas berkontribusi terhadap pengalaman pengguna. Aplikasi yang responsif dan efisien memberikan pengalaman pengguna yang lebih baik. Re-render yang tidak perlu atau keterlambatan dalam pembaruan UI dapat mengganggu alur kerja pengguna dan menurunkan kesan mereka terhadap aplikasi.
Meskipun begitu, penting untuk diingat bahwa useCallback
harus digunakan secara bijaksana. Tidak semua fungsi perlu di-memoize, dan penggunaan useCallback
secara berlebihan atau tanpa kebutuhan yang jelas bisa menambah kompleksitas tanpa manfaat yang signifikan. Penggunaan useCallback
idealnya diinformasikan oleh pemahaman yang baik tentang bagaimana aplikasi Anda beroperasi dan di mana bottleneck performa terjadi.
Menggunakan useCallback
dalam situasi nyata, terutama dalam konteks pemanggilan API yang 'expensive' atau berat, memang sangat bermanfaat. Berikut ini adalah contoh komponen React yang melakukan pemanggilan API menggunakan axios
dan useCallback
.
Agar lebih jelas, saya akan menggunakan endpoint API publik dari JSONPlaceholder, yang merupakan layanan online gratis untuk pengujian dan prototyping dengan data JSON palsu.
import React, { useState, useCallback } from 'react';
import axios from 'axios';
export default function DataFetcher() {
const [data, setData] = useState(null);
const [userId, setUserId] = useState(1);
const fetchData = useCallback(async () => {
try {
const response = await axios(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
setData(response.data);
} catch (error) {
console.error('Error fetching data:', error);
}
}, [userId]);
return (
<div>
<input
type="number"
value={userId}
onChange={(e) => setUserId(e.target.value)}
/>
<button onClick={fetchData}>Load User Data</button>
<div>{data && <pre>{JSON.stringify(data, null, 2)}</pre>}</div>
</div>
);
}
Dalam contoh di atas, fungsi fetchData
dibungkus dalam useCallback
dengan userId
sebagai dependensinya. Ini berarti bahwa fetchData
hanya akan dibuat ulang jika userId
berubah, yang membantu mengoptimalkan kinerja terutama dalam kasus-kasus di mana fungsi tersebut melakukan operasi yang berat seperti pemanggilan API.
Kali ini, kita akan membuat komponen yang melakukan pencarian data dari API setiap kali pengguna mengetik kata kunci pencarian. Ini adalah contoh yang bagus karena pencarian biasanya merupakan operasi yang 'expensive', dan kita ingin membatasi jumlah pemanggilan API yang tidak perlu. Kita akan menggunakan useCallback
untuk mengoptimalkan fungsi pencarian:
import React, { useCallback, useEffect, useState } from 'react';
import axios from 'axios';
export default function Search() {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState([]);
const search = useCallback(async () => {
if (searchTerm !== '') {
try {
const response = await axios(
`https://jsonplaceholder.typicode.com/posts?title_like=${searchTerm}`
);
setResults(response.data);
} catch (error) {
console.error('Error during search:', error);
}
}
}, [searchTerm]);
useEffect(() => {
const timerId = setTimeout(() => search(), 500); // Delay 500 ms
return () => clearTimeout(timerId);
}, [search]);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Type to search..."
/>
{results.length > 0 && (
<ul>
{results.map((result, index) => (
<li key={index}>{result?.title}</li>
))}
</ul>
)}
</div>
);
}
Dalam kode ini, useEffect
digunakan untuk menangani logika debounce. Setiap kali search
berubah, yang berarti searchTerm
berubah, useEffect
akan mengatur timeout. Jika searchTerm
berubah lagi dalam waktu 500 ms, timeout sebelumnya dibersihkan dan diatur ulang, sehingga pencarian hanya terjadi setelah pengguna berhenti mengetik selama 500 ms. Ini mengurangi jumlah permintaan yang dibuat ke server dan mengoptimalkan kinerja pencarian.
useCallback
tidak perlu digunakan jikaAda skenario tertentu di mana penggunaan useCallback
tidak perlu dan bahkan bisa menjadi kontraproduktif. Salah satu contoh umum adalah ketika fungsi yang Anda gunakan tidak diteruskan ke komponen anak atau tidak termasuk dalam dependensi useEffect
, useMemo
, atau hook lain yang memerlukan referensi stabil. Dalam kasus seperti ini, manfaat memoization dengan useCallback
mungkin tidak akan sebanding dengan overhead yang ditambahkannya.
Berikut adalah contoh kode di mana useCallback
tidak perlu digunakan:
import React, { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
Dalam contoh ini, fungsi increment
hanya digunakan untuk mengubah state lokal dan tidak diteruskan ke komponen anak atau digunakan sebagai dependensi untuk hook lain. Oleh karena itu, pembuatan ulang fungsi increment
pada setiap render tidak akan berdampak negatif pada performa. Penggunaan useCallback
di sini akan menambahkan kompleksitas yang tidak perlu dan overhead memori tanpa manfaat performa yang nyata.
Alasan mengapa useCallback
tidak perlu dalam situasi ini adalah:
increment
sangat sederhana dan tidak melakukan komputasi yang berat atau operasi yang mahal.increment
tidak diteruskan ke komponen anak atau komponen yang memerlukan referensi stabil, tidak ada risiko re-render yang tidak perlu.useEffect
, useMemo
, atau hook React lainnya, sehingga pembuatan ulang fungsi ini tidak memicu efek samping tak terduga.Dalam pengembangan React, memahami kapan harus menggunakan optimasi seperti useCallback
adalah kunci. Menghindari penggunaan berlebihan dari hook ini dapat membantu menjaga kode Anda tetap bersih dan efisien.
Dalam pengembangan aplikasi React, memahami kapan dan bagaimana menggunakan useCallback
merupakan aspek penting untuk mengoptimalkan performa aplikasi. Penggunaannya sangat berguna dalam situasi tertentu seperti mencegah re-render yang tidak perlu pada komponen anak atau dalam kasus fungsi yang berat dan kompleks. Namun, penting juga untuk mengenali situasi di mana penggunaan useCallback
tidak memberikan manfaat tambahan dan malah menambahkan overhead yang tidak perlu.
Semoga penjelasan dan contoh-contoh yang telah kita bahas tadi memberikan pemahaman yang lebih baik tentang cara kerja dan kegunaan useCallback
dalam React. Jika Anda merasa artikel ini bermanfaat, jangan ragu untuk membagikannya dengan rekan-rekan Anda. Saya Irsyad, dan saya berharap untuk bertemu Anda lagi di pelajaran selanjutnya. Salam coding!
Let's start living like no one can help us in any event, so that when we are helped in certain times, it becomes a plus in itself.