کار با state ها در ری اکت (React)

17 اسفند 1397
درسنامه درس 6 از سری آموزش react (ری اکت)
React-states

امروز می خواهیم ببینیم که کامپوننت های stateful چطور کار می کنند؟ و ما چه موقع و چرا باید از state ها در کامپوننت مان استفاده کنیم.

در هفته اول، هر آنچه که برای شروع برنامه نویسی ری اکت نیاز بود را یاد گرفتیم. با jsxها کار کردیم، اولین کامپوننت مان را ساختیم، رابطه والد-فرزند را توضیح دادیم و کار با پروپرتی ها (Props) در ری اکت را فرا گرفتیم. اما یک قابلیت خیلی مهم و کاربردی باقی مانده که در این درس می خواهیم راجع به آن صحبت کنیم و آن کار با وضعیت یا state ها است.

کار با state ها در ری اکت (React)

ری اکت به دلایلی به ما اجازه تغییر this.props را بر روی کامپوننت ها نمی دهد فرض کنید اگر یک پروپرتی title به کامپوننت Header پاس دهیم و کامپوننت header بتواند آن را تغییر دهد، از کجا بفهمیم پروپرتی title در کامپوننت header چه مقداری دارد؟

نکته مهمی که باید بدانید این است که در کل تغییر دادن متغیری که کامپوننت والد به کامپوننت فرزند ارسال می کند، کار خوبی نیست. با این حال، گاهی اوقات یک کامپوننت نیاز دارد که وضعیت خودش را تغییر دهد. برای مثال یک برچسب  flag active تنظیم کنیم تا اگر یک کامپوننت فرزند نیاز داشت بتواند زمان را در کامپوننت ساعت انتخاب یا بروزرسانی کند.

هر چند پیشنهاد می شود تا حد امکان از props استفاده کنیم، اما گاهی اوقات نیاز داریم که وضعیت یک کامپوننت را نگه داریم.

state ها تنها در کامپوننت اصلی و کامپوننت های فرزند آن قابل دسترس هستند. همانند روشی که ما با آن یک props را به یک کامپوننت ارسال می کردیم، stateها می توانند توسط this.state در یک کامپوننت قابل دسترس باشند. هر زمان که یک وضعیت تغییر کرد (که اینکار توسط تابع this.setState انجام می شود)، کامپوننت رندر خواهد شد. برای مثال فرض کنید یک کامپوننت ساعت داریم که زمان جاری را نشان می دهد.

کامپوننت ساعت در ریکت

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

در عوض می توانیم یک تایمر برای فراخوانی تابع renderدر داخل کامپوننت تنظیم کرده و تنها وضعیت داخلی این کامپوننت را تغییر دهیم.

حال ساخت این کامپوننت را شروع کرده و نام آن را clock می گذاریم. قبل از اینکه stateها را ایجاد کنیم باید ابتدا متد render را در کامپوننت بوجود بیاوریم.

سپس مقدار زمان جاری را گرفته و در متغیرهای زیر می ریزیم، در نهایت بررسی می کنیم که اگر مقدار ساعت از 12 کمتر بود، am در غیر اینصورت pm را به انتهای زمان جاری اضافه کند. مطابق زیر:

class Clock extends React.Component {
  render() {
    const currentTime = new Date(),
          hours = currentTime.getHours(),
          minutes = currentTime.getMinutes(),
          seconds = currentTime.getSeconds(),
          ampm = hours >= 12 ? 'pm' : 'am';

    return (
      <div className="clock">
        {
          hours == 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}

راه حل جایگزین برای ایجاد فاصله زمانی

به عنوان یک راه حل جایگزین می توانیم از کد زیر برای مدیریت فاصله اضافی بین زمان درکامپوننت ساعت استفاده کنیم.

("00" + minutes).slice(-2)

اما ما پیشنهاد می کنیم که از روش قبلی استفاده کنید.

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

برای انجام اینکار، باید مقدار زمان فعلی را در یک state نگهداری کنیم.

پس در ابتدا باید یک مقدار اولیه به state مان نسبت دهیم.

در ES6 با تنظیم یک مقدار برای this.state که در متد constructor انجام می دهیم، وضعیت اولیه کامپوننت ها را مشخص می کنیم.

  constructor(props) {
    super(props);
    this.state = this.getTime();
  }

در خط اول متد سازنده باید همیشه super(props) را فراخوانی کند. اگر اینکار را نکنید، کامپوننت به درستی کار نخواهد کرد و یک خطا دریافت می کنید.

در واقع با دستور super props می خواهیم بگوییم که منظور ما از props همان چیزی است که برای ارسال داده بکار گرفته می شود و super باعث می شود که این دستور از کامپوننت مادر (یعنی کلاس Component) ارث بری کند.

حال که this.state را در کامپوننت clock تعریف کردیم، می توانیم توسط this.state یک ارجاع به آن در تابع render داشته باشیم.

برای اینکار کدهای زیر را در تابع render برای دریافت مقادیر this.state قرار دهید.

class Clock extends React.Component {
  // ...
  render() {
    const {hours, minutes, seconds, ampm} = this.state;
    return (
      <div className="clock">
        {
          hours === 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}

به جای اینکه مستقیم با مقدار داده ها کار کنید، می توانید state کامپوننت ها را بروزرسانی کرده و در حقیقت تابع render را از بخش مدیریت داده ها جدا کنیم.

همچنین برای بروزرسانی وضعیت از یک تابع به نام setState استفاده می کنیم. این تابع باعث رندر شدن کامپوننت خواهد شد.

نکته: ما باید متد setState را روی مقدار this کامپوننت مان فراخوانی کنیم.

در کامپوننت clock از متد setTimeout برای ساخت یک تایمر به منظور بروزرسانی this.state در هر 1000 میلی ثانیه استفاده می کنیم.

class Clock extends React.Component {
  // ...
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }
  // ...
  setTimer() {
    clearTimeout(this.timeout);
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

در داخل تابع updateClock وضعیت یا state را با یک زمان جدید بروزرسانی می کنیم.

class Clock extends React.Component {
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

بعد از قرار دادن کامپوننت در صفحه، هر یک ثانیه (یا 1000 میلی ثانیه) یکبار مقدار زمان جاری را بروزرسانی می شود، با این حال دوباره به وضعیت قبل خود بر نمی گردد. در قدم بعد باید تابع setTimer را در انتهای متد فراخوانی کنیم.

class Clock extends React.Component {
  // ...
  updateClock() {
    const currentTime = new Date();
    this.setState({
      currentTime: currentTime
    })
    this.setTimer();
  }
  // ...
}

حال امکان دارد خود کامپوننت نسبت به متد Timeout آهسته تر رندر شود که اینکار باعث بروز خطا و رندر غیرضروری می شود.

به جای فراخوانی متد setTimer ، بعد از this.setState می توانیم یک آرگومان دوم به متد this.setState ارسال کرده تا مطمئن شویم که متد setTimer بعد از این که state بروزرسانی شد، فراخوانی می شود.

class Clock extends React.Component {
  // ...
  updateClock() {
    const currentTime = new Date();
    this.setState({
      currentTime: currentTime
    }, this.setTimer);
  }
  // ...
}

کامپوننت ساعت در ریکت

چند نکته که باید به یاد داشته باشید:

  • هنگامی که ما متد setState را با یک آرگومان آبجکتی فراخوانی کنیم، توسط this.state می توانیم به آبجکت ها دسترسی داشته باشیم و سپس کامپوننت را رندر کنیم.
  • ما معمولا تنها می خواهیم که مقادیر state یی که در تابع render از آن استفاده می کنیم را نگه داریم. همان طور که در مثال بالا دیدید ما hours (ساعت)، minutes (دقیقه) و seconds (ثانیه) را در state مان ذخیره کردیم. ذخیره کردن آبجکت یا دستورات محاسباتی در stateیی که قصد نداریم در تابع render از آن استفاده کنیم، کارخوبی نیست، چون اینکار باعث رندر غیرضروری کامپوننت می شود و اتلاف چرخه cpu خواهد شد.

همان طور که در ابتدای این بخش گفتیم، تا حد امکان از props استفاده کنید، چون نه تنها از نظر کارایی اینکار بهتر است، بلکه تست کردن کامپوننت های stateful دشوارتر است.

در این درس ما کامپوننت مان را به حالت stateful در آوردیم و یاد گرفتیم که چطور در مواقع لزوم یک کامپوننت stateful را مدیریت کنیم. در درس بعدی به بحث درباره چرخه حیات یک کامپوننت و نحوه تعامل با آن در یک صفحه می پردازیم.

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

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

محمد
16 فروردین 1400
توضیحات ناقصه getTime() از کجا میاد ؟ this.timeOut چه جوری ساخته میشه ؟ وقتی این کدارو وارد می کنی اصلا درست کار نمی کنه

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