فصل ضمیمه: کار با useState

Appendix: Working with useState

22 بهمن 1399
139-React-The-Complete-Guide

ما در طول این دوره آموزشی در مورد بعضی از hook ها به صورت خلاصه صحبت کردیم اما به شما گفتم که در فصل مخصوص به خودشان درباره شان صحبت خواهیم کرد که همین فصل ضمیمه منظور ما بود. در این جلسه می خواهیم در مورد یکی از مهم ترین hook های react به نام useState صحبت کنیم. همانطور که می دانید این hook به ما اجازه می دهد که در کامپوننت های کاربردی از state استفاده کنیم.

برای شروع کار ابتدا وارد ingredientForm.js شوید. در این فایل یک فرم را مشاهده می کنید و اگر یادتان باشد برای فرم ها از two way binding استفاده می کردیم، یعنی input خود را به یک state متصل می کردیم، سپس با هر کلیدی که توسط کاربر فشرده می شد state را به روز رسانی می کردیم و در نهایت مقدار را دوباره به input برمی گرداندیم. در این قسمت نیز باید همین کار را انجام بدهیم تا بتوانیم داده های کاربر را دریافت کنیم. نگاهی به این فرم بیندازید:

const IngredientForm = React.memo(props => {
  const submitHandler = event => {
    event.preventDefault();
    // ...
  };

  return (
    <section className="ingredient-form">
      <Card>
        <form onSubmit={submitHandler}>
          <div className="form-control">
            <label htmlFor="title">Name</label>
            <input type="text" id="title" />
          </div>
          <div className="form-control">
            <label htmlFor="amount">Amount</label>
            <input type="number" id="amount" />
          </div>
          <div className="ingredient-form__actions">
            <button type="submit">Add Ingredient</button>
          </div>
        </form>
      </Card>
    </section>
  );
});

از آنجایی که این کامپوننت (ingredientForm.js) یک کامپوننت کاربردی است، اگر می خواستیم از state استفاده کنیم و نسخه جدید react را نداشتیم باید کل کامپوننت را به یک کامپوننت کلاس محور تبدیل می کردیم اما حالا با استفاده از react hooks نیازی به این کار نیست. در وهله اول باید useState را وارد این فایل کنیم:

import React, { useState } from 'react';

نحوه استفاده از این hook بدین شکل است که باید آن را درون کامپوننت کاربردی خود صدا بزنید و یک state اولیه را برایش تعریف کنید. این state اولیه ممکن است شیء باشد یا آرایه باشد یا عدد باشد یا رشته باشد و الی آخر. در صورتی که اگر یادتان باشد در کامپوننت های کلاس محور، State همیشه یک شیء بود اما اینجا اینطور نیست. البته من برای راحتی کار از قالب اشیاء استفاده می کنم:

const IngredientForm = React.memo(props => {
  useState({ title: '', amount: '' });

  const submitHandler = event => {
// بقیه کدها //

همانطور که می بینید دو خصوصیت برای State خودمان تعریف کردم: یکی title و دیگری amount که هر دو در ابتدا برابر یک رشته خالی هستند. Title در واقع «عنوان» یا همان اولین input ما و amount «تعداد» یا دومین input ما در فرم است. این شیء state اولیه ما است که بدین شکل ساخته می شود. ممکن است بگویید از آنجایی که amount با اعداد کار می کند بهتر است آن را روی 0 بگذاریم نه روی یک رشته اما از آنجایی که amount قرار است از سمت کاربر دریافت شود و مقدار input ها همیشه به صورت رشته برگردانده می شود، استفاده از رشته ها راحت تر است.

البته هنوز یک قدم باقی مانده است و صدا زدن useState به تنهایی باعث ایجاد state نمی شود. اگر موس خود را روی useState ببرید در پیامی طولانی می بینید که صدا زدن useState باعث برگردانده شدن یک آرایه می شود که دو عضو دارد. useState همیشه یک آرایه با دو عضو ثابت را برمی گرداند که اولین عضو، نمود state شما در حال حاضر است.

هر بار که state شما به روز رسانی می شود کل کامپوننت ما از نو ساخته می شود (عملیات re-render) اما react برای اینکه state شما حذف نشود، آن را در درون خود نگه می دارد و به عبارتی state شما را جدا از کامپوننت نگهداری می کند تا در هر بار به روز رسانی تمام state به حالت اولیه برنگردد. بنابراین عضو اول آرایه برگردانده شده توسط useState همان state فعلی شما است و اگر state خود را به روز رسانی کنید، همان State به روز رسانی شده است. عضو دوم برگردانده شده نیز تابعی است که به شما اجازه می دهد state خود را به روز رسانی کنید. من می خواهم این مقدار برگردانده شده را درون یک ثابت ذخیره کنم:

  const inputState = useState({ title: '', amount: '' });

حالا از two way binding  استفاده می کنیم تا input ها را به State متصل کنیم. من مقدار value از input اول خودم را به شکل زیر تنظیم می کنم:

<label htmlFor="title">Name</label>
<input
  type="text"
  id="title"
  value={inputState[0].title}
/>

همانطور که گفتم state فعلی ما اولین عضو از inputState است که یعنی ایندکس صفر، تا اینجا به اولین عضو آرایه یا state فعلی خود دسترسی پیدا کرده ایم اما state ما یک شیء است بنابراین با اضافه کردن title. به مقدار اول (title) می رسیم. همین کار را برای amount (دومین input) نیز انجام می دهیم:

<label htmlFor="amount">Amount</label>
<input
  type="number"
  id="amount"
  value={inputState[0].amount}
/>

قسمت بعدی two way binding این بود که با تایپ کاربر، state به روز رسانی شود تا از آن طرف مقدار value در این input ها نیز به روز رسانی شود بنابراین می توانیم از onChange استفاده کنیم تا state خود را به روز رسانی کنیم:

onChange={event => inputState[1]()}

همانطور که می دانید اگر از onChange استفاده کنیم، event به صورت خودکار به ما پاس داده می شود و من هم با استفاده از عضو دوم آرایه (ایندکس یک) که یک تابع بود، به آن دسترسی پیدا کرده و آن را اجرا کرده ام. حالا برای تعیین state جدید باید خصوصیت title رادر یک شیء جدید به این تابع پاس بدهیم:

<label htmlFor="title">Name</label>
<input
  type="text"
  id="title"
  value={inputState[0].title}
  onChange={event => inputState[1]({title: event.target.value})}
/>

event.target که همان input ما است و value هم مقدار آن بنابراین همین منطق را برای input دوم نیز پیاده می کنیم:

<label htmlFor="amount">Amount</label>
<input
  type="number"
  id="amount"
  value={inputState[0].amount}
  onChange={event => inputState[1]({ amount: event.target.value })}
/>

اگر کدهایمان را ذخیره کرده و به مرورگر برویم، توانایی تایپ در input ها را داریم که خبر خوبی است و نشان می دهد کدهایمان کار می کنند اما در کنسول مرورگر یک خطا دریافت می کنیم. حتی اگر در اولین input خود تایپ کنیم یک خطا در مورد input دوم که از نوع number است دریافت می کنیم. چرا؟

در جلسه بعد این مورد را بررسی خواهیم کرد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.