Halo, Kodinger! 👋
Jadi, kamu sudah paham useState dan useEffect. Sekarang kamu melirik useCallback dan bertanya-tanya, “Apakah semua fungsi harus saya bungkus pakai ini?”
Jawaban singkat: Enggak. Jawaban panjang: Baca terus ya. ☕
Saya ingat waktu pertama kali belajar Hooks, saya pakai useCallback di mana-mana. Saya pikir itu bikin aplikasi saya “lebih cepat”. Spoiler: Ternyata enggak. Saya malah bikin kode jadi ribet.
Mari kita bedah hook ini bareng-bareng.
Masalahnya: Fungsi itu Beda Terus
Di JavaScript, fungsi itu adalah objek. Dan setiap kali komponen React melakukan re-render, semua fungsi di dalamnya dibuat ulang (re-created).
const MyComponent = () => {
// Fungsi ini BARU setiap kali render
const handleClick = () => {
console.log("Di-klik!");
};
return <ChildComponent onClick={handleClick} />;
};
Biasanya, ini nggak masalah. Browser itu cepat kok.
Tapi, kalau ChildComponent dibungkus pakai React.memo (biar nggak render ulang kalau props nggak berubah), dia bakal ngecek apakah props-nya berubah.
Karena handleClick secara teknis adalah fungsi yang baru setiap saat, React.memo mikirnya props-nya berubah, dan akhirnya render ulang si child. Usaha optimasi jadi sia-sia deh. 📉
Solusinya: useCallback
useCallback bilang ke React: “Woy, jagain fungsi ini biar tetep sama antar render, kecuali ada hal spesifik yang berubah.”
import { useCallback, useState } from "react";
const MyComponent = () => {
const [count, setCount] = useState(0);
// Sekarang, fungsi ini tetep sama!
const handleClick = useCallback(() => {
console.log("Di-klik!");
}, []); // <--- Dependency array
return <ChildComponent onClick={handleClick} />;
};
Sekarang, ChildComponent melihat referensi fungsi yang persis sama. Kalau dia pakai React.memo, dia nggak bakal render ulang yang nggak perlu.
Kapan sebaiknya dipakai?
Jangan asal pakai ya (premature optimization). Ini aturan main saya:
- Oper props ke komponen yang di-memo: Kalau kamu ngasih fungsi ke list yang panjang atau komponen berat yang dibungkus
React.memo, pakai ini. - Dependency di useEffect: Kalau sebuah fungsi masuk ke dalam dependency array
useEffect, bungkus pakaiuseCallbackbiar nggak memicu effect setiap kali render.
Contoh: Jebakan “Too Many Renders”
Bayangkan kamu punya useEffect yang ambil data pas fungsi fetchData dipanggil.
// TANPA useCallback
const fetchData = () => { ... };
useEffect(() => {
fetchData();
}, [fetchData]); // 🚨 Awas Infinite Loop!
Karena fetchData itu baru setiap render, effect-nya nganggap itu “berubah” dan jalan lagi… yang bikin re-render… yang bikin fetchData baru… dan duar. Infinite loop. 💥
Benerin pakai useCallback:
// PAKAI useCallback
const fetchData = useCallback(() => { ... }, []);
useEffect(() => {
fetchData();
}, [fetchData]); // ✅ Aman sentosa
Penutup
Optimasi itu pedang bermata dua. useCallback juga ada “biaya”-nya (React harus nyimpen fungsi di memori). Gunakan cuma kalau ada masalah performa yang jelas atau butuh referensi fungsi yang stabil, bukan cuma biar kelihatan keren.
Keep it simple! 🚀