State چیست و چگونه با useState از آن استفاده کنیم

تا اینجا با Props آشنا شدیم؛ دیدیم Props داده‌ای است که از بیرون به یک کامپوننت داده می‌شود و آن کامپوننت نمی‌تواند آن را تغییر دهد. اما بسیاری از رابط‌های کاربری نیاز دارند خودشان داده‌ای را نگه دارند که در طول زمان و بر اساس تعامل کاربر تغییر می‌کند؛ مثلاً مقدار یک شمارنده، باز یا بسته بودن یک منو، یا متن وارد‌شده در یک input. این نوع داده در React، State نام دارد.

چرا نمی‌توان از یک متغیر معمولی استفاده کرد

فرض کنید بخواهیم یک شمارنده بسازیم و برای نگهداری مقدار آن از یک متغیر معمولی جاوااسکریپت استفاده کنیم:

function Counter() {
  let count = 0;

  function handleClick() {
    count = count + 1;
    console.log(count); // مقدار درست چاپ می‌شود
  }

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>افزایش</button>
    </div>
  );
}

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

راه‌حل: Hook به نام useState

برای اینکه React از تغییر یک مقدار باخبر شود و کامپوننت را دوباره رندر کند، باید آن مقدار را با useState تعریف کنیم. useState یک Hook است (با مفهوم کلی Hookها در فصل بعدی آشنا می‌شویم) که به کامپوننت اجازه می‌دهد مقداری را نگه دارد و هر بار که آن مقدار تغییر کند، رندر مجدد را درخواست دهد.

import { useState } from "react";

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

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

  return (
    <div>
      <p>{count}</p>
      <button onClick={handleClick}>افزایش</button>
    </div>
  );
}

تحلیل نحو useState

فراخوانی useState(0) یک آرایه با دو عضو بازمی‌گرداند: عضو اول، مقدار فعلی State است (در اینجا count)، و عضو دوم، تابعی است برای درخواست تغییر آن مقدار (در اینجا setCount). عدد 0 که به useState داده شده، مقدار اولیه State است و فقط در اولین رندر کامپوننت استفاده می‌شود.

نام‌گذاری [count, setCount] از طریق Array Destructuring انجام می‌شود و نام‌ها دلخواه هستند؛ اما قرارداد رایج در جامعه React این است که تابع تغییردهنده را با پیشوند set و سپس نام State بنویسیم.

چرا باید از setCount استفاده کنیم، نه تغییر مستقیم

نکته مهم این است که هرگز نباید مقدار State را مستقیماً تغییر داد (مثلاً count = count + 1)؛ باید همیشه از تابعی که useState برگردانده استفاده کرد. فقط فراخوانی این تابع است که به React اطلاع می‌دهد باید کامپوننت را دوباره رندر کند. تغییر مستقیم متغیر، هیچ‌گونه رندر مجددی را ایجاد نمی‌کند، حتی اگر مقدار در حافظه واقعاً عوض شده باشد.

هر کامپوننت، State مستقل خودش را دارد

اگر از یک کامپوننت دارای State، چند نمونه مختلف در صفحه استفاده کنیم، هرکدام State کاملاً مستقل و جداگانه‌ای خواهند داشت:

function App() {
  return (
    <div>
      <Counter />
      <Counter />
    </div>
  );
}

در این مثال، تغییر مقدار شمارنده اول، هیچ تأثیری روی شمارنده دوم ندارد؛ چون هرکدام نمونه جداگانه‌ای از State خودشان را دارند.

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

یک شمارنده ساده با useState
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

State مقداری است که یک کامپوننت خودش نگه می‌دارد و در طول زمان تغییر می‌کند. برخلاف یک متغیر معمولی جاوااسکریپت، تغییر State باید همیشه از طریق تابعی که useState برمی‌گرداند انجام شود؛ فقط در این صورت React متوجه تغییر می‌شود و کامپوننت را دوباره رندر می‌کند. در درس بعدی به بررسی دقیق‌تر نحوه به‌روزرسانی State و مفهوم Functional Updates می‌پردازیم.