هوک useCallback (useCallback)
هوک useCallback در ری اکت کمک می کند تابع ها را کش (Cache) کنی تا هر بار بی خودی دوباره ساخته نشوند. این کار مخصوصاً وقتی کامپوننت های زیادی داری، رندرهای اضافه را کم می کند.
useCallback در ری اکت دقیقاً چه کار می کند؟
هوک (Hook) یعنی تابع کمکی ری اکت که به کامپوننت قدرت اضافه می کند. useCallback در ری اکت یک تابع را «مموایز (Memoize)» می کند؛ یعنی خودش را نگه می دارد تا وقتی وابستگی ها عوض نشدند، دوباره ساخته نشود.
وابستگی (Dependency) یعنی مقدارهایی که اگر عوض شوند، تابع باید دوباره ساخته شود. این تابع معمولاً همان هندلر کلیک یا تابعی است که به فرزند می فرستی.
useMemo در ری اکت مقدار مموایز شده برمی گرداند. اما useCallback در ری اکت تابع مموایز شده برمی گرداند. پس هر دو شبیه اند، فقط خروجی متفاوت است.
اگر خواستی جزئیات useMemo را ببینی، بعداً سر بزن به صفحه هوک useMemo.
سینتکس useCallback در ری اکت
طبق منبع، useCallback دو آرگومان اصلی دارد: خود تابع و آرایه وابستگی ها. اگر یکی از وابستگی ها عوض شود، تابع دوباره ساخته می شود.
useCallback(callback, dependencies);
callback همان تابعی است که می خواهی مموایز شود. dependencies آرایه ای از مقدارهاست که تغییرشان باعث ساخت نسخه تازه تابع می شود.
مثال بدون استفاده از useCallback در ری اکت
در مثال اول، یک والد و دو دکمه داری. هر دکمه یک تابع onClick می گیرد. توابع در هر رندر دوباره ساخته می شوند. با اینکه از React.memo برای فرزند استفاده شده، باز هم هر بار هر سه کامپوننت رندر می شوند.
کد مثال بدون useCallback در ری اکت
//Without useCallback:
import React, { useState } from "react";
import { createRoot } from "react-dom/client";
// Child component that receives a function prop
const Button = React.memo(({ onClick, text }) => {
alert(`Child ${text} button rendered`);
return <button onClick={onClick}>{text}</button>;
});
// Parent component without useCallback
function WithoutCallbackExample() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// This function is recreated on every render
const handleClick1 = () => {
setCount1(count1 + 1);
};
const handleClick2 = () => {
setCount2(count2 + 1);
};
alert("Parent rendered");
return (
<div>
<h2>Without useCallback:</h2>
<p>Count 1: {count1}</p>
<p>Count 2: {count2}</p>
<Button onClick={handleClick1} text="Button 1" />
<Button onClick={handleClick2} text="Button 2" />
</div>
);
}
createRoot(document.getElementById("root")).render(
<WithoutCallbackExample />
);
طبق توضیح منبع، اگر این کد را اجرا کنی و روی دکمه ها کلیک کنی، والد و هر دو دکمه دوباره رندر می شوند. حتی وقتی فقط یکی از شمارنده ها عوض شده است.
نکته: اینجا مشکل اصلی این است که توابع handleClick1 و handleClick2 در هر رندر نسخه تازه ای می سازند. این کار باعث می شود React.memo روی کامپوننت Button خیلی کمک نکند.
استفاده از useCallback در ری اکت برای بهینه سازی
در نسخه دوم مثال، useCallback در ری اکت استفاده شده است. این بار توابع کلیک با useCallback ساخته می شوند و فقط وقتی شمارنده مربوطه عوض شود، دوباره ساخته می شوند.
کد مثال با useCallback در ری اکت
//With useCallback:
import React, { useState, useCallback } from "react";
import { createRoot } from "react-dom/client";
// Child component that receives a function prop
const Button = React.memo(({ onClick, text }) => {
console.log(`${text} button rendered`);
return <button onClick={onClick}>{text}</button>;
});
// Parent component with useCallback
function WithCallbackExample() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// These functions are memoized and only recreated when dependencies change
const handleClick1 = useCallback(() => {
setCount1(count1 + 1);
}, [count1]);
const handleClick2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
console.log("Parent rendered");
return (
<div>
<h2>With useCallback:</h2>
<p>Count 1: {count1}</p>
<p>Count 2: {count2}</p>
<Button onClick={handleClick1} text="Button 1" />
<Button onClick={handleClick2} text="Button 2" />
</div>
);
}
createRoot(document.getElementById("root")).render(
<WithCallbackExample />
);
منبع می گوید حالا اگر روی Button 1 کلیک کنی، فقط والد و همان دکمه رندر می شوند. اگر روی Button 2 کلیک کنی، فقط والد و Button 2 رندر می شوند.
پس useCallback در ری اکت کمک می کند که توابعی که به فرزندها می فرستی، فقط وقتی لازم است عوض شوند. این یعنی رندر کمتر و کار سبک تر برای مرورگر.
اگر خواستی این مبحث را دوباره مرور کنی، صفحه useCallback در ری اکت در UnderDevelops را به عنوان مرجع نگه دار.
جمع بندی سریع
- useCallback در ری اکت تابع را مموایز می کند، نه مقدار را.
- این هوک دو ورودی دارد: خود تابع و وابستگی ها.
- تابع فقط وقتی وابستگی ها عوض شوند، دوباره ساخته می شود.
- کنار React.memo روی فرزند، رندرهای اضافه را کم می کند.
- در لیست ها و کامپوننت های تو در تو، بسیار به دردبخور است.