هوک useEffect ری اکت و بررسی تخصصی React.useEffect
Published: 2023-11-10 18:29:27



هوک useEffect ری اکت یکی از حیاتی ترین هوک های React هست. حقیقتا اصلا مهم نیست که چند وقته سراغ React اومدید ، useEffect یکی از ضروری ترین مباحثی هست که هر ری اکت دِوِلوپری باید بهش تسط کامل داشته باشه !!

با فِرانت اِندی همراه باشید که میخوایم این هوک خفن رو بررسی کنیم 🙂

قبل از هرچیز ، بهتره ببینیم خود ری اکت چه تعریفی از این هوک خفنش داره :
 هوک useEffect به شما امکان مدیریت Side Effect ها را میدهد ! 



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6opnqe0sor8jslt3kskx.png)


اما Side Effect یعنی چی ؟!

خود React میگه که از هوک useEffect میشه برای مدیریت Side Effect های کامپوننت استفاده کرد.

بطور ساده و خلاصه ، هر رفتار یا عملی که باعث تاثیر خارجی در Function شود را Side Effect میگوییم.

تو ری اکت معمولا چنین Side Effect هایی رو داریم :

    دریافت Data از سرور :
    زمانیکه ما از سرور یکسری اطلاعات دریافت میکنیم و در صفحه نمایش میدیم. این اتفاق نمونه بارز Side Effect هست.
    تغییر DOM :
    وقتی میخوایم یک تغییر در DOM ایجاد کنیم مثل اضافه کردن Event Listener یا تغییر Element ها. این موارد هم یک نوع Side Effect هستن.
    خواندن اطلاعات از Local Storage :
    خوندن اطلاعات ذخیره شده در Local Storage هم یک نوع Side Effect محسوب میشه.
    چند نمونه دیگر :
    اضافه کردن setTimeOut ، استفاده از سوکت و ابزار های بروزرسانی real time نیز Side Effect محسوب میشن.

تمام موارد بالا یک نوع Side Effect هستن.

در واقع Side Effect ها غیرقابل پیش بینی هستن چون با محیط خارجی در ارتباط هستن! مثلا برای صفحه وبلاگ قصد دریافت لیست مقالات از سرور را داریم اما ارور 500 دریافت میکنیم!

Side Effect ها معمولا عوارض جانبی واسه اپیکیشن ما دارن مثل Memory Leaks ( پر شدن حافظه رم ) و رفتار غیرمنتظره و پیش بینی نشده.

پس با توجه به عوارضی که Side Effect ها دارن ، خیلی ضروریه که بتونیم مدیریتشون کنیم!

هوک useEffect ری اکت اومده تا باهاش بتونیم Side Effect های ری اکت رو مدیریت کنیم 🙂



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rg8xn4ry5nmqt7530zo.png)


هوک useEffect ری اکت چیست ؟

تو React ، هوک useEffect به ما امکان مدیریت Side Effect هارو میده ( مواردی که در بالا مثال زدیم مثل Data Fetching )

همچنین هوک useEffect امکان مدیریت چرخه حیات ( lifecycle ) کامپوننت رو بهمون میده. ( مثلا اینکه زمانیکه کامپوننت ایجاد میشه ، re-Render میشه یا از DOM حذف میشه چه اتفاقی بیوفته )

مدیریت چرخه حیات کامپوننت باعث میشه که رفتار کامپوننت ما قابل پیش بینی باشه.

قبل از اینکه بریم هوک useEffect در ری اکت رو بررسی کنیم، باید نکته زیر رو برای خودمون یادآوری کنیم:

اِفِکت ها تو ری اکت بعد از هر re-Render اتفاق میوفتن البته میشه جلوی این موضوع رو گرفت.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5b3ziiku8qtfx6i2us2k.png)


چطور از هوک useEffect استفاده کنیم ؟

چگونگی استفاده از هوک useEffect در ری اکت بصورت زیر هست :

`useEffect(callback, [dependencies]);`



یا :

`// 1. ایمپورت کردن هوک یوز افکت
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 پاس دادیم اما هر نوع عملیاتی میشه داخلش نوشت.

    دومین آرگومان Dependencies هست که باید در قالب یک آرایه به 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 ری اکت

اگه قصد استفاده از useEffect در React رو دارید ، باید یکسری نکات مهم رو رعایت کنید.
اجرا شدن useEffect بعد هر re-Render

اگه آرایه Dependencies ( آرگومان دوم useEffect ) رو قرار ندید ، تابعی که داخل useEffect قرار دادید بعد از هر re-Render اجرا میشه. ( اجرا بعد از هر re-Render )
لوپ بینهایت در useEffect !

اگه هیچ 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)) // ساید افکت ما
  },[]); // آرایه خالی یعنی فقط بعد اولین رندر اجرا شو
}`


تابع CleanUp در ری اکت

گاهی اوغات میخوایم وقتی کامپوننت ما از بین میره ( از 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 تیکه کد بالا اجرا میشه.

 
مثال کاربردی از هوک useEffect

ابتدای همین مقاله گفتیم که از هوک useEffect برای مدیریت Side Effect ها استفاده میکنیم.

تو مثال زیر یک Side Effect رو تو بدنه کامپوننت اجرا کردیم که این اشتباهه. اگه از useEffect برای اجرای Side Effect ها استفاده نکنیم امکان مدیریت Side Effect هارو نداریم!

`function Greet({ name }) {
  const message = `Hello, ${name}!`;

  // این بده
  document.title = `Greetings to ${name}`; // ساید افکت ما

  return <div>{message}</div>;
}`




تو تیکه کد بالا یک Side Effect داریم .

خط 5 با هر بار re-Render شدن کامپوننت اجرا میشه و این اصلا خوب نیست چون این Side Effect باید فقط درصورت تغییر مقدار name که یک Props هست ، اجرا بشه.

برای رفع این مشکل و مدیریت Side Effect بطوریکه فقط درصورت تغییر Props ، رندر مجدد صورت بگیره باید بصورت زیر عمل کنیم :


`import { useEffect } from 'react';

function Greet({ name }) {
  const message = `Hello, ${name}!`;

  useEffect(() => {
    // Good!
    document.title = `Greetings to ${name}`; الان ساید افکت ما فقط درصورت تغییر نام اجرا میشه و این عالیه
  }, [name]);

  return <div>{message}</div>;
}`



بریم چند نمونه از کاربرد هوک useEffect در React رو ببینیم …



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ib821bvbwt05ksvdcpy.png)


با هوک useEffect ری اکت چیکار میشه کرد ؟

بطور خلاصه و ساده ما میتونیم با هوک useEffect ری اکت ، یکسری کارها و عملیات مواقع زیر انجام بدیم :

    هنگام ایجاد شدن کامپوننت در DOM یا همون Component did mount

بطور مثال وقتی کاربر وارد صفحه X شد یک پیغام بهش نشون بدیم. یا وقتی کاربر وارد صفحه فروشگاه شد ، لیست محصولات رو از سرور دریافت کنیم. بصورت زیر :
 

`useEffect(() => {
  // فقط زمان ایجاد کامپوننت اجرا میشم
  document.title = 'کامپوننت ایجاد شد';
  axios.get('/blogs') // دریافت لیست مقالات از سرور
}, []);`



تو تیکه کد بالا ، آرایه خالی به عنوان Dependencies باعث شده که تابع فقط 1 بار پس از اولین رندر اجرا بشه و تو re-Render های بعدی کامپوننت اجرا نشه.

    هنگام تغییر مقدار Value هایی که خودمون مشخص کردیم به عنوان Dependencies ( همون Component did update )

مثلا درصورت تغییر سبد خرید کاربر ، سبد خرید جدید رو داخل Local Storage ذخیره کنیم. ( در این صورت باید cart رو به عنوان Dependencies برای useEffect قرار بدیم تا فقط درصورت تغییر cart این اتفاق رخ بده )
تو مثال زیر گفتیم هر زمان props تغییر کرد ، callback Function ما مجدد اجرا بشه :


`useEffect(() => {
  localstorage.SetItem('cart',JSON.stringify(cart)) // من زمانی که سبد خرید تغییر کنه اجرا میشم
}, [cart]);`




تو Component did update یه نکته ای وجود داره. اینکه اجرای Side Effect یکبار پس از اولین Render صورت میگیره و در دفعات بعد فقط هنگام تغییر مقادیر Dependencies صورت میگیره.

    هنگام حذف شدن کامپوننت از DOM یا همون Unmounting ( بطور مثال این مرحله هنگام تغییر صفحه توسط کاربر اتفاق میوفته )

مثلا میخوایم وقتی کاربر از فروشگاه به صفحه دیگه ای رفت ، یکسری کارها داخل صفحه فروشگاه اتفاق بیوفته.
اگه داخل useEffect یک تابع Return کنیم ، تابع فقط پس از حذف کامپوننت از DOM اجرا میشه :

`useEffect(()=>{

return function cleanUp(){
// من فقط بعد از حذف کامپوننت از دام اجرا میشم
}

},[])`




## ادامه این مقاله 
لطفا برای مطالعه ادامه این مقاله + تیکه کد ها و مثالهای بیشتر روی لینک زیر کلیک کنید : 

[useEffect در ریکت](https://frontendi.com/useeffect-react/)