استفاده از هوک state

هوک‌ها پس از انتشار نسخه‌ی 16.8 به ری‌اکت اضافه شده‌اند. آن‌ها به شما اجازه می‌دهند که از state و دیگر قابلیت های ری‌اکت بدون نوشتن کلاس استفاده کنید.

در صفحه‌ی معرفی از این مثال برای معرفی هوکها استفاده شد:

import React, { useState } from 'react';

function Example() {
  // "count" جدید به نام state تعریف کردن یک متغیر
  const [count, setCount] = useState(0);

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

ما با مقایسه این کد با یک مثال مشابه که بر پایه کلاس نوشته شده‌است، یادگیری هوک‌ها را شروع می‌کنیم.

مثال کلاس مشابه

اگر قبلا از کلاس‌ها در ری‌اکت استفاده کرده‌باشید، این کد باید برای شما آشنا باشد:

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

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

این state در شروع به عنوان { count: 0 } تعریف شده‌است، و ما مقدار state.count را وقتی که کاربر روی دکمه کلیک می‌کند با استفاده از this.setState() افزایش می‌دهیم. ما همواره از این الگو در تمام صفحه استفاده می‌کنیم.

توجه:

شاید برای شما این سوال پیش بیاید که چرا ما استفاده از یک شمارنده(counter) را به استفاده از یک مثال واقعی‌تر ترجیح داده‌ایم. چون این به ما کمک می‌کند تا روی API تمرکز کنیم در حالی که هنوز در اول راه یادگیری هوک‌ها هستیم.

هوک‌ها و کامپوننت‌های تابعی

به‌عنوان یک یادآوری، کامپوننت‌های تابعی در ری‌اکت به شکل زیر هستند:

const Example = (props) => {
  // شما می‌توانید از هوک‌ها در اینجا استفاده کنید!
  return <div />;
}

یا به این شکل:

function Example(props) {
  // شما می‌توانید از هوک‌ها در اینجا استفاده کنید!
  return <div />;
}

شاید شما در گذشته این کامپوننت‌ها را با نام “کامپوننت‌های بدون state” می‌شناختید. ما حالا قابلیتی را معرفی می‌کنیم که با استفاده از آن می‌توانید از state ری‌اکت در این نوع کامپوننت‌ها استفاده کنید، بنابراین ما این نوع کامپوننت ها را “کامپوننت تابعی” می‌نامیم.

شما نمی توانید از هوک‌ها در کلاس‌ها استفاده کنید. ولی با استفاده از آن‌ها دیگر احتیاجی به نوشتن کلاس نخواهید داشت.

هوک چیست؟

مثال جدید خود را با import کردن هوک useState از ری‌اکت شروع می‌کنیم:

import React, { useState } from 'react';

function Example() {
  // ...
}

هوک چیست؟ هوک یک تابع ویژه است که به شما اجازه می‌دهد از امکانات ری‌اکت استفاده کنید. برای مثال، هوک useState به شما این امکان را می‌دهد که از state در کامپوننت‌های تابعی استفاده کنید. هوک‌های دیگری هم وجود دارند که به یادگیری آن‌ها در آموزش‌های بعدی می‌پردازیم.

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

توجه:

چند قانون ویژه درباره‌ این که چه جا‌هایی از کامپوننت می‌توانید یا نمی‌توانید از هوک‌ها استفاده کنید وجود دارد. که باید آن‌ها را در قوانین هوک‌ها یاد بگیریم.

تعریف کردین متغیر state

حلا قصد داریم که مقدار اولیه count را در درون state، عدد 0 تعریف کنیم. بدین منظور، در سازنده‌ی کلاس this.state را معادل {count: 0} تعریف می‌کنیم.

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

در یک کامپوننت تابعی، کلید واژه‌ی this وجود ندارد، پس ما نمی توانیم مقدار this.state را بخوانیم و یا مقداری را به آن اختصاص دهیم. در عوض، ما می‌توانیم از هوک useState به‌طور مستقیم در کامپوننت خود استفاده کنیم:

import React, { useState } from 'react';

function Example() {
  // "count" جدید به نام state تعریف کردن یک متغیر
  const [count, setCount] = useState(0);

با استفاده از useState چه کار‌هایی می‌توان انجام داد؟ هدف کلی آن تعریف کردن “متغیر state” است. ما در اینجا متغیری به نام count داریم ولی می‌توانیم نام دلخواه، مثل banana هم برای آن انتخاب کنیم. این یک راه برای “حفظ” برخی مقادیر میان تماس‌های تابعی است — useState یک راه جدید است که دقیقا امکانات مشابه this.state در کلاس را فراهم می‌کند. به طور معمول، مقادیر موجود در تابع پس از بسته شدن تابع از بین می‌روند ولی متغیر state پس از بسته شدن تابع هم توسط ری‌اکت حفظ می‌شود.

چه چیزی را به عنوان آرگومان به useState پاس می‌دهیم؟ تنها آرگومانی که به هوک useState() می‌دهیم مقدار اولیه‌ی state است. بر خلاف کاری که در کلاس ها انجام می‌دهیم، state حتما نباید یک object باشد. اگر ما به Number یا String احتیاج داریم می توانیم state را به عنوان Number یا String تعریف کنیم. در مثال، ما فقط به یک عدد احتیاج داریم که بفهمیم کاربر چند بار کلیک کرده‌است، پس 0 را به عنوان مقدار اولیه‌ی state خود به useState پاس‌ ‌می‌دهیم. (اگر ما بخواهیم دو مقدار متفاوت را در state ذخیره کنیم، باید دوبار از useState() استفاده کنیم.)

useState چه چیزی را بر می‌گرداند؟ این هوک یک جفت آیتم را بر می‌گرداند: یک state به نام count و یک تابع برای به‌روزرسانی مقدار آن. به این دلیل است که ما آن را به صورت const [count, setCount] = useState() نوشته‌ایم. این مقادیر مشابه this.state.count و this.setState در کلاس کامپوننت هستند، اگر شما با این نوع syntax آشنایی ندارید، ما در پایین این صفحه به آن می‌پردازیم.

حالا که متوجه کاربرد هوک useState شدیم، باید مثال ما را بهتر متوجه شوید:

import React, { useState } from 'react';

function Example() {
  // "count" جدید به نام state تعریف کردن یک متغیر
  const [count, setCount] = useState(0);

ما یک متغیر state به نام count تعریف کرده‌ایم، و مقدار آن را برابر 0 قرار داده‌ایم. ری‌اکت مقدار این state را میان رندر کردن دوباره‌ی کامپوننت حفظ می‌کند، و جدیدترین مقدار آن را برای تابع فراهم می‌کند. اگر ما بخواهم مقدار count را به‌روزرسانی کنیم، می‌توانیم از setCount استفاده کنیم.

توجه:

شاید براتون این سوال پیش بیاد: چرا از اسم useState استفاده کردیم و به جای آن از اسم createState استفاده نکردیم؟

استفاده از نام “Create” بسیار دقیق نخواهد بود چون یک state فقط در زمان اولین رندر یک کامپوننت ساخته می‌شود. در هنگام رندر های بعدی، useState فقط مقدار state را به ما می‌دهد. در غیر این صورت اگر قرار بود در هر رندر یک state با همان مقدار ساخته شود که دیگر state نبودند! یک دلیل دیگر هم وجود دارد همیشه نام هوک‌ها با use شروع می‌شود.دلیل این را هم می‌توانید در قوانین هوک‌ها مطالعه کنید.

خواندن State

وقتی که ما بخواهیم مقدار شمارش فعلی را نمایش دهیم، از this.state.count می‌خوانیم:

  <p>You clicked {this.state.count} times</p>

در تابع، ما می‌توانیم به طور مستقیم از count استفاده کنیم:

  <p>You clicked {count} times</p>

به‌روزرسانی State

در کلاس، ما باید با استفاده از this.setState() مقدار state count را به‌روزرسانی کنیم:

  <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Click me
  </button>

در تابع، ما از قبل setCount و count به عنوان یک متغیر تعریف کرد‌ایم پس دیگر احتیاج به استفاده از کلید واژه‌ی this نداریم:

  <button onClick={() => setCount(count + 1)}>
    Click me
  </button>

خلاصه

حالا بیاید خلاصه چیزهایی که یادگرفته‌ایم را خط به خط برسی کنیم و بفهمیم چه چیزهایی یاد گرفته‌ایم.

 1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(0);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }
  • خط ۱: ما هوک useState را از ری‌اکت import کردیم. که به شما اجازه‌ی استفاده از state را در تابع می‌دهد.
  • خط ۴: در تابع کامپوننت Example، ما با استفاده از هوک useState یک متغیر state تعریف کرده‌ایم. که بر اساس اسم‌هایی که به آن می‌دهیم یک جفت آیتم را بر می‌گرداند. ما اسم متغیر خود را را count گذاشتیم چون مقدار تعداد کلیک‌هایی که روی دکمه می‌کنیم را در خود نگه می‌دارد. ما مقدار اولیه آن را به‌وسیله‌ی پاس دادن 0 به عنوان آرگومان به useState برابر 0 قرار داده‌ایم. مقدار دومی که useState برمی‌گرداند یک تابع است. این تابع به شما اجازه می‌دهد که مقدار count را به‌روزرسانی کنید پس ما اسم آن را setCount می‌گذاریم.
  • خط ۹: وقتی که کاربر روی دکمه کلیک می‌کند، ما با استفاده از setCount مقدار جدید را به state count می‌دهیم. سپس ری‌اکت کامپوننت Example را دوباره رندر می‌کند، و مقدار جدید count را به آن پاس می‌دهد.

حجم مطالب ممکن است باری یادگیری در اولین بار زیاد به نظر برسد. ولی عجله نکنید! اگر شما هر کدام از توضیحات را متوجه نشده‌اید، مثال بالا را دوباره بخوانید و سعی کنید یک بار دیگر مطالب را از بالا به پایین با دقت بخوانید. ما به شما این قول را می‌دهیم اگر یکبار دیگر تلاش به خواندن این مطلب بکنید “فراموش می‌کنید” که state در کلاس‌ها چگونه کار می‌کند.

نکته: کروشه ([]) چه کاری انجام می‌دهد؟

شاید متوجه استفاده از کروشه([]) موقع تعریف متغیر state شده‌باشید:

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

اسم‌های سمت چپ جزئی از API ری‌اکت نیستند. و شما می‌توانید نام دلخواه برای متغیر استفاده کنید:

  const [fruit, setFruit] = useState('banana');

این یک syntax جاوا‌اسکریپت به نام “array destructuring” است. و به این معنی که ما دو متغیر به نام‌های fruit و setFruit را تعریف کرده‌ایم. تابع useState دو مقدار را باز می‌گرداند که به ترتیب به fruit و setFruit اختصاص داده می‌شوند. کد زیر مشابه همین کار را انجام می‌دهد:

  var fruitStateVariable = useState('banana'); // یک جفت آیتم را باز می‌گرداند
  var fruit = fruitStateVariable[0]; // آیتم اول
  var setFruit = fruitStateVariable[1]; // آیتم دوم

وقتی که ما توسط useState یک متغیر state تعریف می‌کنیم، useState یک جفت آیتم را بازمی‌گرداند — یک آرایه با دو آیتم. آیتم اول حاوی مقدار متغیر است، و آیتم دوم یک تابع برای به‌روزرسانی مقدار آن است. استفاده از [0] و [1] برای دسترسی ممکن است مقداری گمراه‌کننده باشد چون ما موقع کار با آرایه‌ از آن‌ها استفاده می‌کنیم برای همین ما به به‌جای آن از array destructuring استفاده می‌کنیم.

توجه:

شاید کنجکاو باشید که بدانید که ری‌اکت چگونه میفهمد که هر useState با کدام کامپوننت مطابقت دارد در صورتی که ما هیچ چیزی مانند کلید واژه‌ی this را به ری‌اکت پاس نمی‌دهیم. ما به این سوال و سوالات دیگر در بخش پرسش و پاسخ جواب داده‌ایم.

نکته: استفاده از چندین متغیر state

تعریف کردن متغیر state به عنوان جفت [something, setSomething] می‌تواند برای ما مفید باشد چون به ما این امکان را می‌دهد که از اسامی مختلف برای نام گذاری state ها استفاده کنیم اگر بخواهیم بیشتر از یک state تعریف کینم:

function ExampleWithManyStates() {
  // state تعریف کردن چندین!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

در کامپوننت بالا، ما متغیر‌های age، fruit و todos را داریم، و می‌توانیم هر کدام را جداگانه به‌روزرسانی کنیم:

  function handleOrangeClick() {
    // this.setState({ fruit: 'orange' }) مشابه
    setFruit('orange');
  }

شما مجبور نیستید متغیرهای state زیادی تعریف کنید. چون متغیرهای state توانایی نگه‌داری object و array را هم دارد، پس می‌توانید مقدارهای مربوط به هم را به عنوان یک گروه تعریف کنید. با این حال، برخلاف this.setState در کلاس، به‌روزرسانی یک متغیر state همیشه به جای ترکیب کردن از جایگزین کردن استفاده می‌کند.

ما توصیه‌های بیشتری را درباره‌ی متغیر state در بخش پرسش و پاسخ فراهم کرده‌ایم.

قدم‌های بعدی

در این صفحه ما یکی از هوک‌ها به نام useStae که توسط ری‌اکت فراهم شده‌است را یاد گرفته‌ایم. همچین ما گاهی اوقات از آن به عنوان “State Hook” یاد می‌کنیم. که به ما این امکان را می‌دهد که از state در کامپوننت تابعی استفاده‌ کنیم — که این امکان برای اولین بار فراهم شده‌است!

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

حالا با یادگیری هوک بعدی: useEffect ادامه دهید که به شما امکانات مشابه method چرخه‌ی حیات که در کلاس کامپوننت وجود دارد را می‌دهد.