استفاده از Effect Hook در React

Hooks (یا قلاب‌ها) قابلیتی است که در آینده نزدیک منتشر شده و به شما اجازه می‌دهد بدون نوشتن یک class از حالت یا قابلیت‌های دیگر React استفاده کنید. این قابلیت در حال حاضر در نسخۀ ۱۶.۷.۰-alpha ری اکت موجود است.

Effect Hook به شما اجازه می‌دهد اثرات جانبی (side effects) را در کامپوننت های تابعی اجرا کنید:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

این قطعه بر مبنای مثال شمارندۀ مطلب قبل (مربوط به State Hook) است، اما یک قابلیت جدید به آن اضافه کرده‌ایم: ما عنوان تابع را یک پیغام سفارشی (custom) شامل تعداد کلیک ها تنظیم کرده‌ایم.

گرفتن اطلاعات، تنظیم اشتراکات، و تغییر دستی DOM در کامپوننت های ری اکت همه مثال‌هایی از اثرات جانبی هستند. چه از فراخوانی این عملیات اثرات جانبی (یا به طور خلاصه اثرات یا effects) استفاده کرده باشید و چه نکرده باشید، احتمالا تا کنون آنها را در کامپوننت های خود اجرا کرده‌اید.

نکته: اگر با متدهای چرخۀ زندگی class ری اکت آشنا هستید، می‌توانید به هوک useEffect به عنوان ترکیبی از componentDidMount و componentDidUpdate و componentWillUnmount فکر کنید.

دو نوع رایج اثرات جانبی در کامپوننت های React وجود دارد: آنهایی که به پاکسازی (cleanup) نیاز ندارند و آنهایی که نیاز دارند. بیایید نگاهی با جزئیات بیشتر به این تمایز داشته باشیم.

اثرات بدون پاکسازی

گاهی اوقات می‌خواهیم چند کد اضافی را پس از اینکه ری اکت DOM را آپدیت کرد اجرا کنیم. درخواست های شبکه، تغییرات دستی DOM و logging مثال‌های رایجی از اثراتی (effects) هستند که نیاز به پاکسازی ندارند. ما این را می‌گوییم چون آنها را اجزا کرده و بلافاصله فراموششان می‌کنیم. بیایید نحوۀ بیان این اثرات جانبی را در class ها و Hook ها با هم مقایسه کنیم.

مثال استفاده از class ها

در کامپوننت های class ری اکت، متد رندر خودش نباید باعث اثرات جانبی شود. در واقع خیلی زود خواهد بود، ما معمولا می‌خواهیم اثرات را پس از اینکه ری اکت DOM را آپدیت کرد اجرا کنیم.

به همین دلیل است که در class های ری‌اکت، ما اثرات جانبی را در componentDidMount و componentDidUpdate قرار می‌دهیم. به مثال خود برگردیم، اینجا یک کامپوننت class شمارندۀ ری اکت داریم که عنوان متن را درست بعد از این که ری اکت تغییرات را در DOM اعمال کرد، آپدیت می‌کند:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

به اینکه چطور ما مجبور هستیم کد را بین دو متد چرخۀ زندگی تکرار کنیم، دقت کنید.

دلیلش این است که در بسیاری از موارد ما می‌خواهیم اثر جانبی یکسانی را بدون توجه به اینکه آیا کامپوننت تازه mount شده یا آپدیت شده اجرا کنیم. به طور مفهومی، می‌خواهیم پس از هر رندر انجام شود اما کامپوننت های class ری اکت چنین متدی ندارند. می‌توانستیم یک متد مجزا استخراج کنیم ولی باز هم می‌بایست آن را دو جا فراخوانی کنیم.

حال بیایید ببینیم همین کار را چگونه می‌توان با هوک useEffect انجام داد.

مثال استفاده از Hook ها

ما این مثال را در بالای همین صفحه دیده‌ایم، اما بیایید نگاه دقیق‌تری داشته باشیم:

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

هوک useEffect چه کار می‌کند؟ با استفاده از این هوک شما به ری اکت می‌گویید که کامپوننت شما نیاز دارد پس از هر رندر کاری را انجام دهد. ری اکت تابعی را که شما منتقل کردید به یاد خواهد داشت (ما به آن تابع به عنوان اثر یا effect اشاره می‌کنیم) و بعدا پس از اجرای آپدیت‌های DOM آن را فراخوانی می‌کنیم. در این effect ما عنوان متن را تنظیم کردیم اما می‌توانستیم API لازم دیگری را فراخوانی کنیم.

چرا useEffect در داخل یک کامپوننت فراخوانی می‌شود؟ قرار دادن useEffect داخل کامپوننت به ما اجازۀ دسترسی به متغیر حالت count (یا هر prop دیگری) را درست از طریق effect می‌دهد. ما نیاز به API خاصی برای خواندن آن نخواهیم داشت و خودش در محدودۀ تابع حضور دارد. Hook ها خاتمه‌های جوا اسکریپت را دربر می‌گیرند و از معرفی API های خاص ری اکت جلوگیری می‌کند.

آیا useEffect پس از هر رندر اجرا می‌شود؟ بله. به طور پیش‌فرض هم بعد از رندر اول و هم بعد از هر آپدیت اجرا می‌شود (بعدا در مورد تنظیم و شخصی سازی این موضوع صحبت خواهیم کرد).به جای فکر کردن در مورد mount کردن و آپدیت کردن، شاید بهتر باشد به رخ دادن اثرات پس از رندر فکر کنید. ری اکت تضمین می‌دهد که وقتی effects اجرا شده‌اند، DOM آپدیت شده است.

توضیح تفصیلی

حال که در مورد effects اطلاعاتی به دست آورده‌ایم، خطوط زیر می‌توانند بهتر معنا دهند:

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

ما متغیر حالت count را تعریف کرده و سپس به ری اکت می‌گوییم که نیاز داریم از یک effect استفاده کنیم. ما یک تابع را به هوک useEffect منتقل می‌کنیم. این تابع، effect ماست. داخل effect ما عنوان متن را با استفاده از API مرورگر document.title تنظیم می‌کنیم. ما می‌توانیم آخرین مقدار count داخل effect را بخوانیم چون در داخل محدودۀ تابع ما قرار دارد. وقتی ری اکت کامپوننت ما را رندر می‌کند، effect مورد استفادۀ ما را به یاد خواهد داشت و سپس پس از هر آپدیت DOM، آن را اجرا می‌کند. این اتفاق در هر رندر رخ خواهد داد، که شامل رندر اول نیز می‌شود.

برنامه نویسان باتجربۀ جاوا اسکریپت احتمالا متوجه خواهند شد که تابع منتقل شده به useEffect در هر رندر متفاوت خواهد بود. این عمدی است. در حقیقت همین موضوع به ما اجازه می‌دهد مقدار count را از داخل effect و بدون نگرانی در مورد هدر رفتن آن بخوانیم. هر بار که رندر مجدد را انجام می‌دهیم، یک effect متفاوت را برنامه ریزی می‌کنیم که جایگزین قبلی می‌گردد. به طریقی، این کار سبب می‌شود effect ها مانند بخشی از نتیجۀ رندر رفتار کنند که هر effect متعلق به یک رندر خاص است. علت سودمند بود این موضوع را در همین صفحه خواهیم دید.

نکته: effect ها بر خلاف componentDidMount و componentDidUpdate مرورگر را از آپدیت صفحه بلاک نمی‌کنند و باعث می‌شود برنامۀ شما پاسخگوتر باشد. اکثریت effect ها نیازی به رخ دادن همزمان ندارند. در برخی موارد نه چندان رایج که نیاز به همزمانی دارند (مانند اندازه گیری layout)، یک هوک دیگر یعنی useLayoutEffect وجود دارد که یک API مشابه useEffect دارد.

اثرات با پاکسازی

پیش از این، به نحوۀ بیان اثرات جانبی بدون نیاز به پاکسازی نگاهی انداختیم. اما برخی effect ها نیاز به پاکسازی خواهند داشت. برای مثال ممکن است بخواهیم یک اشتراک (subscription) به برخی منابع اطلاعاتی خارجی تنظیم کنیم. در این مورد پاکسازی به گونه‌ای که هیج اطلاعاتی درز نکند مهم است. بیایید نحوۀ انجام این کار را در class ها و Hook ها با هم مقایسه کنیم.

مثال استفاده از class ها

در کامپوننت های class ری اکت، معمولا شما اشتراک را در componentDidMount تنظیم کرده و با componentWillUnmount پاکسازی را انجام می‌دهید. برای مثال فرض کنید ما یک ماژول ChatAPI داریم که به ما اجازه می‌دهد در وضعیت آنلاین بودن دوستمان شریک شویم. نحوۀ شریک شدن و نمایش آن وضعیت با استفاده از یک class به این صورت است:

class FriendStatus extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }

  render() {
    if (this.state.isOnline === null) {
      return 'Loading...';
    }
    return this.state.isOnline ? 'Online' : 'Offline';
  }
}

دقت کنید که چگونه componentDidMount و componentWillUnmount نیاز دارند تا یکدیگر را منعکس کنند. متدهای چرخۀ زندگی ما را به جداسازی چنین منطق‌هایی می‌کنند حتی اگر از نظر مفهومی کد بین آنها مرتبط با اثر مشترکی باشد.

نکته: خوانندگان تیزبین ممکن است متوجه شده باشند که این مثال برای اینکه به طور کامل درست کار کند نیاز به یک متد componentDidUpdate نیز دارد. ما فعلا از این موضوع می‌گذریم اما در بخش دیگری از همین صفحه به آن بازخواهیم گشت.

مثال استفاده از Hook ها

بیایید ببینیم چگونه می‌شود همین کامپوننت را با Hook ها نوشت.

ممکن است فکر کنید که ما به یک effect مجزا برای اجرای پاکسازی نیاز داریم. اما کد اضافه کردن و حذف کردن یک اشتراک آنقدر به هم مرتبط هستند که در طراحی useEffect هردوی آنها درنظر گرفته شده است. اگر effect شما یک تابع را برگرداند، ری اکت وقتی زمان پاکسازی‌اش باشد آن را اجرا می‌کند:

import { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }

  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // Specify how to clean up after this effect:
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

چرا ما یک تابع را از effect برگرداندیم؟ این مکانیزم پاکسازی انتخابی برای effect هاست. هر effectی می‌تواند تابعی را برگرداند که آن را پاکسازی کند. این کار به ما اجازه می‌دهد منطق را برای اضافه کردن و حذف کردن اشتراکات نزدیک به هم نگه داریم. آنها بخشی‌هایی از یک effect مشترک هستند.

ری اکت دقیقا چه زمانی پاکسازی را بعد از یک effect انجام می‌دهد؟ ری اکت پاکیازی را پس از اینکه کامپوننت unmounts شد اجرا می‌کند. هرچند، همانطور که قبلا هم یاد گرفتیم، effect ها برای هر رندر اجرا می‌شوند، نه فقط یک بار. به همین دلیل است که ری اکت effect را از رندر قبلی نیز پاکسازی می‌کند، قبل از این که effect ها را برای مراحل بعد اجرا کند. در مورد اینکه چگونه این کار از به وجود آمدن باگ‌ها جلوگیری می‌کند و چگونه در مواقعی که این رفتار باعث مشکلاتی می‌شود از آن جلوگیری کنیم، در همین صفحه صحبت خواهیم کرد.

نکته: حتما نیاز نیست که یک تابع نامگذاری شده را از effect برگردانیم. ما در این مثال آن را cleanup تا هدفمان را روشن بیان کنیم، اما شما می‌توانید یک تابع پیکانی را برگردانید و یا نام دیگری برای آن برگزینید.

خلاصه

ما یاد گرفتیم که useEffect به ما اجازه می‌دهد انواع مختلفی از اثرات جانبی را پس از رندر یک کامپوننت بیان کنیم. برخی effect ها ممکن است به پاکسازی نیاز داشته باشند و یک تابع را برگردانند:

useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

برخی دیگر از effect ها ممکن است فاز پاکسازی را نداشته باشند و چیری را برنگردانند:

useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

هوک Effect هردوی این حالات را تلفیق کرده و یک API تنها ساخته است.

اگر فکر می‌کنید که به درک مناسبی از Effect Hook و نحوۀ کار آن رسیده‌اید، همین الان می‌توانید به بخش بعدی که مربوط به قوانین Hook ها است بروید.

نکاتی در مورد استفاده از Effect ها

در ادامۀ این صفحه قصد داریم نگاه عمیق‌تری به برخی جنبه‌های useEffect که کاربران باتجربۀ React ممکن است در مورد آنها کنجکاو باشند بپردازیم. البته مجبور نیستید همین الان این مطالب رو مورد بررسی قرار دهید. هر زمان که بخواهید می‌توانید به این صفحه برگردید تا اطلاعات بیشتری در مورد هوک useEffect بدست آورید.

استفاده از چند Effect جهت تقسیم موارد

یکی از مشکلاتی که ما در قسمت انگیزه به وجود آوردن Hook ها بیان کردیم این بود که متدهای چرخۀ زندگی class اغلب شامل منطق‌های نامرتبط است، اما منطق‌های مرتبط در چندین متد به چند قسمت تقسیم می‌شوند. در اینجا مثالی را آورده‌ایم که شمارنده و وضعیت آنلاین بودن دوست را از مثال‌های قبلی، با هم ترکیب می‌کند:

class FriendStatusWithCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0, isOnline: null };
    this.handleStatusChange = this.handleStatusChange.bind(this);
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  handleStatusChange(status) {
    this.setState({
      isOnline: status.isOnline
    });
  }
  // ...

دقت کنید که چگونه منطق تنظیم کنندۀ document.title بین componentDidMount و componentDidUpdate تقسیم شده است. منطق subscription نیز بین componentDidMount و componentWillUnmount منتشر شده است و componentDidMount کد مربوط به هر دو امر را در بر دارد.

خب، Hook ها چگونه این مشکل را حل می‌کنند؟ دقیقا همانطور که شما می‌توانید از State Hook بیش از یک بار استفاده کنید، از چند effect نیز می‌توانید استفاده کنید. این موضوع به ما اجازه می‌دهد منطق‌های نامرتبط را در effect های مجزا از هم تفکیک کنیم:

function FriendStatusWithCounter(props) {
  const [count, setCount] = useState(0);
  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  // ...
}

هوک ها به ما اجازه می‌دهند کدها را بر مبنای اینکه چه کاری انجام می‌دهند از هم تفکیک کنیم، نه این که متد آنها چه نامی دارد. ری اکت تمام effect های استفاده شده در یک کامپوننت را به ترتیبی که به آنها اختصاص داده شده است اعمال می‌کند.

چرا effect ها در هر آپدیت اجرا می‌شوند؟

اگر به کار کردن با class ها عادت کرده باشید، ممکن است برایتان جای تعجب باشد که چرا effect فاز پاکسازی را بع داز هر رندر مجدد انجام می‌دهد و نه فقط حین unmounts کردن. بیایید به یک مثال کاربردی نگاه کنیم تا ببینیم چرا این گونه طراحی به ما در ساخت کامپوننت هایی با باگ های کمتر کمک می‌کند.

قبلا در همین صفحه مثالی از کامپوننت FriendStatus را معرفی کردیم که وضعیت آنلاین بودن یک دوست را نشان می‌داد. در این مثال class ما friend.id را از this.props می‌خواند، پس از mount شدن کامپوننت به وضعیت دوست subscribe می‌کند و حین unmounts شدن unsubscribe می‌کند:

componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

اما وقتی prop دوست هنگامی که کامپوننت در حال نمایش داده شدن در صفحه است، تغییر کند چه اتفاقی خواهد افتاد؟ کامپوننت ما نمایش وضعیت آنلاین یک دوست دیگر را نمایش می‌داد. این یک باگ است. همچنین ممکن است باعث نشت حافظه یا در هم ریختن هنگام unmounting شویم چون فراخوانی unsubscribe از آی‌دی دوست اشتباهی استفاده می‌کند.

در یک کامپوننت class ما نیاز به اضافه کردن componentDidUpdate جهت کنترل کردن این مورد خواهیم داشت:

componentDidMount() {
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate(prevProps) {
    // Unsubscribe from the previous friend.id
    ChatAPI.unsubscribeFromFriendStatus(
      prevProps.friend.id,
      this.handleStatusChange
    );
    // Subscribe to the next friend.id
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

فراموش کردن استفاده درست از componentDidUpdate منبع باگ‌های رایجی در برنامه‌های React است.

اکنون نسخه‌ای از این کامپوننت را ببینید که از Hook ها استفاده کرده است:

function FriendStatus(props) {
  // ...
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

این کامپوننت دیگر آن باگ را نخواهد داشت (با وجود اینکه تغییر خاصی در آن ایجاد نکرده‌ایم).

در این کامپوننت نیاز به کد خاصی جهت کنترل آپدیت‌ها نیست چون useEffect خودش به طور پیش‌فرض آن را کنترل می‌کند. این هوک effect های قبلی را پیش از اعمال کردن effect های بعدی، پاکسازی می‌کند. به عنوان توضیح می‌توان گفت اینجا توالی فراخوانی‌های subscribe و unsubscribe را داریم که می‌توانند به وسیلۀ کامپوننت تولید شوند:

// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // Run first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // Run next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // Run next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect

این رفتار ثبات برنامه را به طور پیش‌فرض تضمین می‌کند و از باگ‌هایی که در کامپوننت های class بسیار رایج است جلوگیری می‌کند.

بهینه سازی عملکرد با Skip کردن effect ها

در برخی موارد ممکن است پاکسازی یا اعمال effect بعد از هر رندر در عملکرد برنامه مشکلی ایجاد کند. در کامپوننت های class می‌توانیم این موضوع را با نوشتن یک مقایسۀ اضافی به کمک prevProps یا prevState در داخل componentDidUpdate مشکل را حل کنیم:

componentDidUpdate(prevProps, prevState) {
  if (prevState.count !== this.state.count) {
    document.title = `You clicked ${this.state.count} times`;
  }
}

این مسئله به اندازه‌ای رایج است که در داخل هوک useEffect قرار داده شده است. شما می‌توانید به React بگویید که در صورتی که مقادیر خاصی بین رندرهای مجدد تغییری نداشته‌اند، اعمال کردن effect را skip کند. برای انجام این کار، یک آرایه را به عنوان آرگومان دوم اختیاری به useEffect منتقل کنید:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

در مثال بالا ما [count] را به عنوان آرگومان دوم منتقل کردیم. این کار معنایش چیست؟ اگر count  مقدارش ۵ باشد، و سپس کامپوننت با مقداری هنوز برابر ۵ رندر مجدد را انجام دهد، ری اکت [۵] از رندر قبلی را با [۵] از رندر بعدی مقایسه می‌کند. چون تمام آیتم‌های درون آرایه با هم برابر هستند (۵ === ۵) ری اکت React را skip می‌کند. این باعث بهینه سازی عملکرد خواهد شد.

وقتی ما با count  آپدیت شده، رندر را انجام می‌دهیم، ری اکت [۵] از رندر قبلی را با [۶] از رندر بعدی مقایسه می‌کند و این بار effect اجرا می‌شود چون ۵ !== ۶. اگر چند آیتم درون آرایه موجود باشد، اگر تنها یکی از آیتم‌ها متفاوت بوده باشد، ری اکت effect را اجرا خواهد کرد.

این موضوع برای effect هایی که عمل پاکسازی را دارند نیز صادق است:

useEffect(() => {
  ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
  return () => {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  };
}, [props.friend.id]); // Only re-subscribe if props.friend.id changes

در آینده، ممکن است آرگومان دوم به طور خودکار به وسیلۀ تبدیل حین ساخت اضافه شود.

نکته: اگر از این بهینه سازی استفاده می‌کنید، مطمئن شوید که آرایه شامل هر مقداری از دامنه بیرونی که در طول زمان تغییر می‌کند و توسط effect مورد استفاده قرار می‌گیرد باشد. در غیر این صورت کد شما مقادیر بیات شدۀ رندرهای قبلی را به عنوان مرجع در نظر می‌گیرد. ما در مورد بهینه سازی‌های دیگر در بخش مرجع Hooks API سحبت خواهیم کرد.

اگر می‌خواهید فقط یک بار یک effect را اجرا کرده و پاکسازی را انجام دهید (هنگام mount یا unmounts)، می‌توانید یک ارایۀ خالی را ([])به عنوان آرگومان دوم منتقل کنید. این کار به ری اکت می‌گوید که effect شما وابسته به هیچ مقداری از prop ها یا حالت نیست و در نتیجه هرگز به اجرا شدن مجدد نیاز ندارد. این موضوع یک حالت ویژه‌ای نیست، بلکه دقیقا مانند همان روشی است که آرایه‌های ورودی همیشه کار می‌کردند. با وجود اینکه منتقل کردن [] شبیه روش کار componentDidMount و componentWillUnmount، اما توصیه ما این است که به استفاده کرده از آن عادت نکنید چون گاهی باعث به وجود آمدن مشکلاتی می‌شود.

گام‌های بعدی

بسیار عالی. این صفحه طولانی شد اما در نهایت به بسیاری از سوالات شما در رابطه با effect ها پاسخ داده شد. تا کنون شما State Hook و Effect Hook را یاد گرفته‌اید و با ترکیب آنها کارهای زیادی را می‌توانید انجام دهید. این دو اکثر موارد استفادۀ class ها را پوشش خواهند داد و جایی که این دو نتوانند، احتمالا Hook دیگری را می‌توانید پیدا کنید که به شما کمک کنند.

همچنین قصد داریم در مورد اینکه Hook ها چگونه مشکلاتی را که در قسمت انگیزه آنها را بررسی کردیم، صحبت کنیم. ما دیدیم که چگونه پاکسازی effect از تکرار در componentDidUpdate و componentWillUnmount جلوگیری می‌کند، کدهای مرتبط را به هم نزدیک‌تر می‌کند و از له وجود آمدن باگ‌ها جلوگیری می‌کند. همچنین در مورد اینکه چگونه می‌توانیم effect ها را بر مبنای هدف استفاده از آنها از هم جدا کنیم صحبت کردیم، چیزی که در class ها قادر به انجام آن نبودیم.

حال ممکن است برایتان سوال باشد که Hook ها چگونه کار می‌کنند. چگونه ری اکت متوجه می‌شود که کدام فراخوانی useState متناظر با کدام متغیر حالت بین رندرهای مجدد است؟ چگونه ری اکت در هر آپدیت effect های قبلی و بعدی را تطبیق می‌دهد؟ در صفحۀ بعدی ما قوانین Hook ها بررسی خواهیم کرد که برای درست کار کردن Hook ها ضروری هستند.

1 Star2 Stars3 Stars4 Stars5 Stars (3 votes, average: 4٫67 out of 5)
Loading...
counter customizable free hit