هوک useEffect ری اکت یکی از حیاتی ترین هوک های React هست. حقیقتا اصلا مهم نیست که چند وقته سراغ React اومدید ، useEffect یکی از ضروری ترین مباحثی هست که هر ری اکت دِوِلوپری باید بهش تسط کامل داشته باشه !!
با فِرانت اِندی همراه باشید که میخوایم این هوک خفن رو بررسی کنیم ?
قبل از هرچیز ، بهتره ببینیم خود ری اکت چه تعریفی از این هوک خفنش داره :
هوک useEffect به شما امکان مدیریت Side Effect ها را میدهد
خود React میگه که از هوک useEffect میشه برای مدیریت Side Effect های کامپوننت استفاده کرد.
بطور ساده و خلاصه ، هر رفتار یا عملی که باعث تاثیر خارجی در Function شود را Side Effect میگوییم.
تو ری اکت معمولا چنین Side Effect هایی رو داریم :
تمام موارد بالا یک نوع Side Effect هستن.
در واقع Side Effect ها غیرقابل پیش بینی هستن چون با محیط خارجی در ارتباط هستن! مثلا برای صفحه وبلاگ قصد دریافت لیست مقالات از سرور را داریم اما ارور 500 دریافت میکنیم!
ساید افکت ها معمولا عوارض جانبی واسه اپیکیشن ما دارن مثل Memory Leaks ( پر شدن حافظه رم ) و رفتار غیرمنتظره و پیش بینی نشده.
پس با توجه به عوارضی که Side Effect ها دارن ، خیلی ضروریه که بتونیم مدیریتشون کنیم!
هوک useEffect ری اکت اومده تا باهاش بتونیم Side Effect های ری اکت رو مدیریت کنیم ?
تو React ، هوک useEffect به ما امکان مدیریت Side Effect هارو میده ( مواردی که در بالا مثال زدیم مثل Data Fetching )
همچنین هوک useEffect امکان مدیریت چرخه حیات ( lifecycle ) کامپوننت رو بهمون میده. ( مثلا اینکه زمانیکه کامپوننت ایجاد میشه ، re-Render میشه یا از DOM حذف میشه چه اتفاقی بیوفته )
مدیریت چرخه حیات کامپوننت باعث میشه که رفتار کامپوننت ما قابل پیش بینی باشه.
قبل از اینکه بریم هوک useEffect در ری اکت رو بررسی کنیم، باید نکته زیر رو برای خودمون یادآوری کنیم:
اِفِکت ها تو ری اکت بعد از هر re-Render اتفاق میوفتن البته میشه جلوی این موضوع رو گرفت.
چگونگی استفاده از هوک useEffect در ری اکت بصورت زیر هست :
useEffect(callback, [dependencies]);
یا :
import { useEffect } from 'react'; // تابع ما const App = () => { useEffect(() => { }, []); return(<div>FrontEndi.com</div>) }
تو خط 2 ما هوک useEffect رو از خود react فراخوانی کردیم.
تو خط 6 داخل کامپوننت خودمون از هوک useEffect استفاده کردیم.
خود هوک useEffect از ما 2 ورودی میگیره:
تابعی که به useEffect پاس میدیم یک Callback Function هست. این تابع بعد از Render کامپوننت اجرا میشه.
تو این تابع میتونیم Side Effect های خودمون رو اجرا کنیم. ( مثل دریافت اطلاعات از سرور و .. )
تابع callback دقیقا بعد از بروزرسانی DOM اجرا میشه. تو تیکه کد بالا یک تابع خالی به useEffect پاس دادیم اما هر نوع عملیاتی میشه داخلش نوشت.
دومین آرگومان که اختیاری هست ، یک آرایه هست. این آرایه وابستگی های useEffect هست.
این آرایه باید شامل مقادیری باشه که میتونن رو Side Effect ما تاثیر بزارن .
در واقع اگه این وابستگی ها مقدارشون تغییر کنه ، Side Effect ما مجدد اجرا میشه. به مثال زیر دقت کنید :
import { useEffect } from 'react'; // کامپوننت ما const App = ({name,email}) => { // استفاده از هوک یوز افکت useEffect(() => { axios.get('mysite.com/api // My Side Effect') }, [name,email]); // useEffect Dependencies return(<div>FrontEndi.com</div>) }
تو خط 8 آرایه Dependencies رو تعریف کردیم. خط 8 این معنی رو میده که اگر مقدار name و email تغییر کردن Side Effect ما ( خط 7 ) باید مجدد اجرا بشه.
اگه بخوایم دقیقتر خط 8 رو بررسی کنیم میتونیم اینو بگیم :
بین re-Render های مختلف کامپوننت ، هوک useEffect مقادیر Dependencies رو چک میکنه که ببینه آیا تغییری داشتن یا نه ؟ اگه بدون تغییر بودن که هیچ. اما اگه مقدارشون تغییر پیدا کرده بود ، تابع داخل useEffect یا همون Side Effect مجدد اجرا میشه.
این اتفاق خیلی خوبه چون خیلی وقت ها پیش میاد که میخوایم درصورت تغییر یک مقدار ، یک کاری رو انجام بدیم. مثلا درصورت تغییر اطلاعات کاربر ، اطلاعات جدید کاربر رو به سرور یا Local Storage بفرستیم.
اگه قصد استفاده از useEffect در React رو دارید ، باید یکسری نکات مهم رو رعایت کنید.
اگه آرایه Dependencies ( آرگومان دوم useEffect ) رو قرار ندید ، تابعی که داخل useEffect قرار دادید بعد از هر re-Render اجرا میشه. ( اجرا بعد از هر re-Render )
اگه هیچ Dependencies برای useEffect تعریف نکنید و داخل تابع useEffect یک State رو بروزرسانی کنید ، به این دلیل که پس از هر تغییر State یک re-Render صورت میگیره و مجدد تابع useEffect اجرا میشه و مجدد State بروزرسانی میشه ، کامپوننت شما داخل یک Loop بینهایت میوفته و برنامه شما کرش میکنه! ( Infinite Loop )
تو تیکه کد زیر این مشکل واضحه :
function MyComponent() { const [data, setData] = useState([]) useEffect(() => { fetchData().then(myData => setData(myData)) // اینجا استیت رو آپدیت میکنیم که باعث ری رندر میشه }); // اینجا آرایه وابستگی تعریف نشده. پس بعد هر ری رندر اجرا میشه }
تو تیکه کد بالا اتفاق زیر میوفته :
برای اولین بار Render صورت میگیره و تابع داخل useEffect اجرا میشه. داخل این تابع State رو آپدیت کردیم و چون State تغییر میکنه ، مجدد کامپوننت re-Render میشه. همین پروسه تا روز قیامت ادامه پیدا میکنه ?
برای اینکه اتفاق بالا نیوفته ، باید یک آرایه خالی به عنوان وابستگی به useEffect پاس بدیم.
زمانیکه یک آرایه خالی ( Empty Array ) به هوک useEffect پاس میدیم، باعث میشه که تابع داخل useEffect فقط بعد از اولین Render اجرا بشه. ( یعنی یکبار )
مثال زیر ، شکل تصحیح شده تیکه کد بالاست :
function MyComponent() { const [data, setData] = useState([]) useEffect(() => { fetchData().then(myData => setData(myData)) // ساید افکت ما },[]); // آرایه خالی یعنی فقط بعد اولین رندر اجرا شو }
گاهی اوغات میخوایم وقتی کامپوننت ما از بین میره ( از DOM حذف میشه ) یک اتفاقی بیوفته.
مثلا سناریویی رو در نظر بگیرید که داخل useEffect یک تایمر گذاشتیم اما کامپوننت ما از DOM حذف میشه. اتفاقی که اینجا میوفته این هست که کامپوننت ما از DOM حذف میشه اما تایمر هنوز داره کار میکنه چون clearInterval نشده!
کد زیر کد مشکل دار هست چون تابع cleanUp نداره :
function Timer() { const [time, setTime] = useState(0); useEffect(() => { setInterval(() => setTime(1), 1000); // یه تایمر داریم که هر یک ثانیه یکبار اجرا میشه اما هیچموقع متوقفش نکردیم }, []); }
مشکلی که تو تیکه کد بالا داریم این هست که :
زمانیکه کامپوننت از DOM حذف میشه ، setInterval همچنان کار میکنه و setTime رو انجام میده! ( درصورتیکه کامپوننت حذف شده )
این اتفاق به این دلیل میوفته که ما clearInterval نکردیم. ( تایمر رو متوقف نکردیم ) پس در اینجا به مشکل پر شدن حافظه در ری اکت بر میخوریم ( Memory Leak )
برای رفع این مشکل باید یک تابع Clean Up بنویسیم. یعنی زمانیکه کامپوننت ما از DOM حذف شد یک کاری انجام بدیم.
در این سناریو باید پس از حذف کامپوننت از DOM ، عملیات ClearInterval رو انجام بدیم .
برای اینکه اینکار رو انجام بدیم باید داخل useEffect ، یک تابع Return کنیم :
function Timer() { const [time, setTime] = useState(0); useEffect(() => { let interval = setInterval(() => setTime(1), 1000); return () => { // setInterval cleared when component unmounts clearInterval(interval); } }, []); }
اگه تو useEffect یک تابع Return کنید ، خود useEffect متوجه میشه که این تابع رو فقط در زمان حذف کامپوننت از DOM باید اجرا کنه.
خط 8 و 9 تیکه کد بالا فقط بعد از حذف شدن کامپوننت از DOM ، اجرا میشه.
مثلا زمانیکه از کاربر از یک صفحه به صفحه دیگه منتقل میشه ، unmounted کامپوننت رخ میده خط 8 و 9 تیکه کد بالا اجرا میشه.
برای مطالعه ادامه این مقاله + مثال ها و کد های بیشتر ، لطفا روی لینک زیر کلیک کنید :