اپلیکیشن‌های React از کامپوننت‌ها ساخته می‌شوند و هر کامپوننت زندگی خاص خود را دارد. روی صفحه ظاهر می‌شود، به اطلاعات جدید واکنش نشان می‌دهد و در نهایت ناپدید می‌شود. اگر این ریتم را نادیده بگیرید، هزینه‌ی واقعی آن را خواهید پرداخت. نشت حافظه (Memory leaks) رخ می‌دهد، عملکرد رندر (Render performance) کاهش می‌یابد و کاربران روی دکمه‌هایی کلیک می‌کنند که دیگر پاسخگو نیستند، زیرا کامپوننتی که به آن‌ها متصل بود ده دقیقه پیش از صفحه خارج (unmount) شده، اما شنونده‌ی رویداد (event listener) همچنان فعال است. درک چرخه حیات (lifecycle) کامپوننت یک تمرین آکادمیک نیست؛ بلکه زیربنای نوشتن نرم‌افزاری است که در استفاده‌ی واقعی، قابل اعتماد باقی بماند.

وجود یک کامپوننت را در سه مرحله تصور کنید. توسعه‌دهندگان اغلب آن‌ها را به عنوان تولد، رشد و مرگ توصیف می‌کنند. React آن‌ها را mounting، updating و unmounting می‌نامد. هر مرحله وظیفه‌ی مشخصی دارد و هر کدام لحظه‌ی دقیقی را برای اجرای کد یا پاکسازی آن در اختیار شما قرار می‌دهند.

سه مرحله اصلی

Mounting همان تولد است. کامپوننت ساخته شده و برای اولین بار در DOM مرورگر قرار می‌گیرد. این پنجره‌ی تنظیمات (setup) شماست. اگر کامپوننت به داده‌های راه دور نیاز داشته باشد، این زمان مناسب برای درخواست آن‌هاست. اگر به یک state اولیه نیاز دارد که از props محاسبه می‌شود، این کار را اینجا انجام می‌دهید. در عصر کامپوننت‌های کلاس (class component)، این وظیفه بر عهده‌ی componentDidMount بود. در کامپوننت‌های تابعی (functional components) مدرن، شما آن را با useEffect و یک آرایه‌ی وابستگی (dependency array) خالی مدیریت می‌کنید. نکته‌ی کلیدی این است که این بلوک از کد تنها یک بار، بلافاصله پس از اینکه React عنصر را در صفحه قرار داد، اجرا می‌شود. این ویژگی آن را برای تنظیمات یک‌باره، مانند دریافت پروفایل کاربر یا خواندن مقداری از localStorage ایده‌آل می‌کند.

Updating همان رشد است. یک کامپوننتِ mounted، ساکن نمی‌ماند. props از یک والد دریافت می‌شوند، state در اثر تعامل کاربر تغییر می‌کند، یا خودِ والد دوباره رندر می‌شود و فرزند را مجبور به پیروی می‌کند. سپس React کامپوننت را دوباره رندر می‌کند تا رابط کاربری (UI) را با داده‌های شما همگام نگه دارد. این مرحله در طول عمر یک کامپوننت بارها تکرار می‌شود. هر بار که یک وابستگی تغییر کند، effectهای شما دوباره اجرا می‌شوند و خروجی جدیدی را به DOM تزریق می‌کنند. از آنجایی که این مرحله بسیار زیاد تکرار می‌شود، محل شروع اکثر مشکلات عملکردی نیز هست. کارهای غیرضروری در این مرحله به سرعت انباشته می‌شوند.

Unmounting همان مرگ است. کامپوننت از صفحه خارج می‌شود، شاید به این دلیل که کاربر به صفحه‌ی دیگری رفته یا یک رندر شرطی (conditional render) آن را خاموش کرده است. در این نقطه، کامپوننت از بین می‌رود، اما دنیای بیرون همیشه این را نمی‌داند. تایمرها همچنان کار می‌کنند، WebSocketها همچنان در حال گوش دادن هستند و event listenerها روی شیء window همچنان متصل می‌مانند. اگر این‌ها را پاکسازی نکنید، آن‌ها به کامپوننت‌های مرده ارجاع می‌دهند و باعث نشت حافظه می‌شوند. مرورگر انرژی خود را صرف «روح‌ها» می‌کند. React دقیقاً به شما یک تابع پاکسازی (cleanup function) می‌دهد تا بتوانید قبل از اینکه کامپوننت برای همیشه ناپدید شود، اینتروال‌ها را متوقف کنید، اتصالات را ببندید و شنونده‌ها را حذف کنید.

از متدهای کلاس تا هوک‌ها

سال‌ها بود که مهندسان React این مراحل را از طریق کامپوننت‌های کلاس مدیریت می‌کردند. منطق برنامه در متدهای مجزا تقسیم شده بود. شما داده‌ها را داخل componentDidMount دریافت می‌کردید، به تغییرات prop در componentDidUpdate پاسخ می‌دادید و در componentWillUnmount محیط را پاکسازی می‌کردید. مشکل این بود که منطق‌های مرتبط پراکنده می‌شدند. یک ویژگی واحد، مانند اتصال به یک چت زنده، برای اتصال، بررسی تغییرات رشته (thread) و قطع اتصال، به کدی در سه متد مختلف چرخه حیات نیاز داشت.

هوک‌ها (Hooks) این وضعیت را تغییر دادند. هوک useEffect در کامپوننت‌های تابعی به شما اجازه می‌دهد منطق را به جای مراحل چرخه حیات، بر اساس هدف گروه‌بندی کنید. با این حال، اگر بدانید کجا را باید نگاه کنید، مدل ذهنی همچنان به خوبی قابل نگاشت است.

برای مدیریت mounting، بنویسید: useEffect(() => { ... }, []). آرایه‌ی وابستگی خالی به React می‌گوید که این effect هیچ وابستگی خارجی ندارد، بنابراین فقط پس از رندر اولیه اجرا می‌شود. این جایگزین componentDidMount می‌شود.

برای مدیریت آپدیت‌ها، مقادیر خاصی را که React باید زیر نظر بگیرد، وارد کنید: useEffect(() => { ... }, [dependency]). وقتی dependency بین رندرها تغییر کند،