هوک useEffect (useEffect)
هوک useEffect در ری اکت کمک می کند کارهای جانبی یا ساید افکت (Side Effect) را انجام بدهی. مثلا گرفتن داده از سرور، تغییر مستقیم DOM، یا ساختن تایمر. با useEffect در ری اکت می توانی بگویی «این کار را بعد از هر رندر، یا فقط بعضی وقت ها انجام بده».
هوک useEffect در ری اکت چیست؟
ساید افکت یعنی کاری خارج از نمایش ساده UI. مثلا درخواست به سرور، تایمر، یا گوش دادن به رویدادهای صفحه.
هوک useEffect در ری اکت تابعی است که به شکل useEffect(fn, deps) صدا زده می شود. آرگومان اول یک تابع است؛ آرگومان دوم یک آرایه وابستگی است و اختیاری است.
اگر آرایه وابستگی را درست تنظیم کنی، دقیقا کنترل می کنی useEffect در ری اکت چه زمانی اجرا شود.
مثال تایمر با useEffect در ری اکت
بیایید از یک تایمر ساده شروع کنیم. در این مثال، هر ثانیه مقدار count یک واحد بیشتر می شود.
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => {
return count + 1;
});
}, 1000);
});
return (
<h1>
I've rendered {count} times!
</h1>
);
}
createRoot(document.getElementById("root")).render(
<Timer />
);
اینجا useEffect در ری اکت بعد از هر رندر اجرا می شود. چون setCount صدا می زنیم، دوباره رندر می گیرد و باز useEffect اجرا می شود. نتیجه این است که شمارنده مدام بالا می رود، نه فقط یک بار.
آرایه وابستگی useEffect در ری اکت
برای کنترل زمان اجرا، آرگومان دوم useEffect را تنظیم می کنیم. این آرگومان یک آرایه است که به آن آرایه وابستگی (Dependency Array) می گوییم.
اگر چیزی در این آرایه نباشد، یا آرایه خالی، یا مقادیر state و props باشد، رفتار useEffect عوض می شود.
1. بدون آرایه وابستگی
اگر آرگومان دوم را ندهی، useEffect در هر رندر اجرا می شود.
useEffect(() => {
// Runs on every render
});
2. آرایه خالی به عنوان وابستگی
اگر آرایه خالی بدهی، useEffect فقط یک بار اجرا می شود؛ دقیقا بعد از رندر اول.
useEffect(() => {
// Runs only on the first render
}, []);
3. وابسته به props یا state
اگر state یا props داخل آرایه باشند، useEffect در ری اکت روی آن ها واکنش می دهد.
useEffect(() => {
// Runs on the first render
// And any time any dependency value changes
}, [prop, state]);
گام های تمرین با useEffect
- یک کامپوننت Timer ساده بساز.
- داخل آن state با نام count تعریف کن.
- useEffect را بدون آرایه وابستگی بنویس و رفتار را ببین.
- سپس آرایه خالی قرار بده و نتیجه را مقایسه کن.
- در نهایت count را در آرایه وابستگی بگذار و رفتار را بررسی کن.
اصلاح تایمر با آرایه وابستگی خالی
برای این که تایمر فقط یک بار اجرا شود، کافی است آرایه وابستگی خالی بدهیم. این یعنی «فقط بعد از اولین رندر اجرا شو».
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
setTimeout(() => {
setCount((count) => {
return count + 1;
});
}, 1000);
}, []);
return (
<h1>
I've rendered {count} times!
</h1>
);
}
createRoot(document.getElementById("root")).render(
<Timer />
);
حالا تایمر فقط یک بار بعد از رندر اول اجرا می شود. count یک بار زیاد می شود و تمام.
مثال useEffect وابسته به state
گاهی می خواهی هر وقت یک state عوض شد، چیزی دوباره محاسبه شود. مثلا یک مقدار محاسباتی بر اساس count.
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
function Counter() {
const [count, setCount] = useState(0);
const [calculation, setCalculation] = useState(0);
useEffect(() => {
setCalculation(() => {
return count * 2;
});
}, [count]);
return (
<>
<p>
Count: {count}
</p>
<button
onClick={() => {
setCount((c) => {
return c + 1;
});
}}
>
+
</button>
<p>
Calculation: {calculation}
</p>
</>
);
}
createRoot(document.getElementById("root")).render(
<Counter />
);
اینجا useEffect در ری اکت وقتی اجرا می شود که count تغییر کند. در هر کلیک، count زیاد می شود و calculation دوباره حساب می شود.
نکته: اگر چند وابستگی داری، همه را داخل آرایه وابستگی useEffect بنویس.
پاک سازی اثرها در useEffect (Effect Cleanup)
بعضی افکت ها باید بعدا تمیز شوند. مثلا تایمرها، event listenerها و اشتراک ها. اگر تمیز نشوند، ممکن است حافظه اضافی مصرف کنند.
برای پاک سازی، از داخل useEffect یک تابع برگردان. این تابع هنگام حذف کامپوننت یا تغییر وابستگی ها اجرا می شود.
import { useState, useEffect } from "react";
import { createRoot } from "react-dom/client";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount((count) => {
return count + 1;
});
}, 1000);
return () => {
clearTimeout(timer);
};
}, []);
return (
<h1>
I've rendered {count} times!
</h1>
);
}
createRoot(document.getElementById("root")).render(
<Timer />
);
برای این که بتوانیم تایمر را پاک کنیم، آن را در متغیر timer نگه داشتیم. بعد در تابع برگشتی clearTimeout را صدا زدیم.
اگر هنوز با state راحت نیستی، پیشنهاد می کنم اول صفحه هوک useState در ری اکت را خوب بخوانی. بعد دوباره سراغ این صفحه برگرد.
همچنین برای دیدن تصویر کامل تر، صفحه هوکس در ری اکت را هم ببین تا بدانی useEffect در کنار بقیه هوک ها کجا قرار می گیرد.
جمع بندی سریع
- هوک useEffect در ری اکت برای انجام ساید افکت ها استفاده می شود.
- آرگومان دوم useEffect در ری اکت زمان اجرای افکت را کنترل می کند.
- آرایه خالی یعنی فقط بعد از رندر اول اجرا شو.
- قرار دادن state در آرایه یعنی بعد از هر تغییر آن state اجرا شو.
- برای تایمر و لیسنرها، همیشه تابع پاک سازی داخل useEffect بنویس.