به‌روزرسانی State و مفهوم Functional Updates

در درس قبلی دیدیم که برای تغییر State باید از تابعی مانند setCount استفاده کنیم. اما نحوه استفاده از این تابع، جزئیات مهمی دارد که اگر نادیده گرفته شوند، می‌توانند باعث رفتارهای غیرمنتظره شوند. در این درس این جزئیات را بررسی می‌کنیم.

State بلافاصله بعد از فراخوانی تغییر نمی‌کند

یک نکته مهم این است که فراخوانی setCount باعث نمی‌شود مقدار count بلافاصله، همان لحظه، در همان اجرای تابع تغییر کند. در عوض، React این درخواست تغییر را ثبت می‌کند و در رندر بعدی، مقدار جدید را در اختیار کامپوننت قرار می‌دهد.

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

  function handleClick() {
    setCount(count + 1);
    console.log(count); // همچنان مقدار قبلی چاپ می‌شود
  }

  return <button onClick={handleClick}>{count}</button>;
}

در این کد، console.log(count) مقدار جدید را نشان نمی‌دهد، چون count در این اجرای خاص تابع، همچنان به مقدار قبلی اشاره دارد. مقدار جدید، فقط در رندر بعدی کامپوننت در دسترس خواهد بود.

مشکل: چند بار فراخوانی setCount با مقدار فعلی

حالا این کد را در نظر بگیرید که تلاش می‌کند مقدار را سه واحد افزایش دهد:

function handleTripleClick() {
  setCount(count + 1);
  setCount(count + 1);
  setCount(count + 1);
}

ممکن است انتظار داشته باشیم مقدار سه واحد افزایش یابد، اما نتیجه واقعی فقط یک واحد افزایش است. دلیل این است که در هر سه فراخوانی، count به همان مقدار اولیه (مثلاً ۰) اشاره دارد؛ پس هر سه فراخوانی درخواست «مقدار را برابر ۱ کن» را ثبت می‌کنند، نه «یکی اضافه کن».

راه‌حل: Functional Update

برای حل این مشکل، به‌جای دادن مقدار جدید بر اساس متغیر فعلی، می‌توان یک تابع به setCount داد. React این تابع را با آخرین مقدار واقعی State فراخوانی می‌کند، نه مقداری که در آن لحظه از اجرای کد در دسترس بوده است:

function handleTripleClick() {
  setCount((prev) => prev + 1);
  setCount((prev) => prev + 1);
  setCount((prev) => prev + 1);
}

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

چه زمانی Functional Update لازم است

استفاده از Functional Update وقتی ضروری است که مقدار جدید State به مقدار قبلی همان State وابسته باشد؛ مخصوصاً در مواردی مانند چند فراخوانی پشت سر هم، یا زمانی که به‌روزرسانی داخل یک Effect یا یک تابع async انجام می‌شود و ممکن است مقدار متغیر محلی، دیگر با آخرین State هماهنگ نباشد. اگر مقدار جدید کاملاً مستقل از مقدار قبلی است (مثلاً یک مقدار ثابت یا داده‌ای از سرور)، نیازی به Functional Update نیست.

مثال قابل اجرا

مقایسه Update معمولی و Functional Update
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

تغییر State از طریق setCount بلافاصله اعمال نمی‌شود و مقدار جدید فقط در رندر بعدی در دسترس است. وقتی مقدار جدید State وابسته به مقدار قبلی آن است، به‌ویژه در فراخوانی‌های پشت سر هم، باید از Functional Update استفاده کرد تا از استفاده مقدار قدیمی جلوگیری شود. در درس بعدی به موضوع State Batching می‌پردازیم که ارتباط نزدیکی با همین مفهوم دارد.