حسان امینی لو
حسان امینی لو
خواندن ۶ دقیقه·۲ سال پیش

استفاده از useReducer به جای useState

چند وقت پیش یه تیکه کد رو گیتهاب دیدم برام خیلی جذاب بود! موضوع این بود که کلا به جای useState داشت از useReducer استفاده می کرد. برام جذاب شد و افتادم دنبالش ببینم چرا باید همچین کاری کنم؟

اعتبار تصویر از kinda code
اعتبار تصویر از kinda code


من چون با context کار نکردم زیاد، فکر کردم که این به درد من نمیخوره چون ایده ام این بود که این برای state management هست و خیلی به کار من نمیاد (که البته هست). ولی وقتی اینو دیدم رفتم داکیومنتش رو خوندم و سورپرایز شدم!

یعنی پشمام! Alternative برای useState؟؟ بعدا متوجه شدم چه کارای خفنی میشه کرد باهاش! تو این مطلب میخوایم ببینیم چجوری باید باهاش کار کنیم.


ایده چیه

فلسفه اینه که به جای چندتا useState مختلف میتونیم از یه دونه useReducer برای همش استفاده کنیم. دقیقا مثل کاری که با Redux و مشابه هاش انجام میدیم ولی خیلی خیلی ساده و جمع و جور تر. به جای setState کردن یه اکشن میدیم بهش و میگیم هر کدوم از state ها با صدا شدن اون action باید یه حالتی داشته باشن. این کمک میکنه که تعداد rerender های صفحه کمتر بشه، از طرف دیگه نیازی نباشه که چندجا هی بگیم setState. یه قابلیت خیییییلی باحال دیگه اینکه که اون dispatcher اش رو میتونیم به هر کامپوننتی خواستیم پاس بدیم و اونجا state رو عوض کنیم. یعنی جایگزین پاس دادن callback های متعدد.

خب توی حرف توضیح دادنش سخته، بریم توی کد ببینیم چیکار باید کرد:

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

ساختار element ها میشه شبیه این:

https://gist.github.com/hesan-aminiloo/f31c1a70b22463f52f818e4e2083a440

از نظر ساختاری و کتابخونه ها و ابزار ها برامون اصلا مهم نیست. برای ما مهم اینه که چه state هایی تو این کامپوننت داریم.

  • یه دونه برای کنترل کردن باز یا بسته بودن modal به اسم modalIsOpen
  • سه تا لازم داریم برای هندل کردم دیتا api که داریم میگیریم: Data, Loading, Error
  • دو تا لازم داریم برای فرمی که توی صفحه هست که یه title داره و یه description

جمعش میشه ۶ تا. یعنی ۶ تا useState تو یک کامپوننت. حالا جذاب تر میشه اگه تابعی که دیتا رو میگیره و این state ها رو هم تغییر میده ببینیم:

https://gist.github.com/hesan-aminiloo/bf62786653a6b1ba47ad718c2be0a680

نگید که ندیدید یا ندارید تو کارتون همچین کدی! همه مون دیدیم و زدیم دیگه! همچنین جایی که useState هاش تعریف شدن:

https://gist.github.com/hesan-aminiloo/cf10a6da204b914071975b68a98a0c21

حالا ممکنه شما خیلی باهوش باشی و بیای بگی که خب میتونستی یه دونه useState بنویسی ولی همش رو تو همون تعریف کنی! خب بله میشد! ولی فرقی نمیکرد بهرحال! دوباره همین آش و همین کاسه بود.

مشکل من با این کد ها اینه که به شدت کامپوننت شون رو شلوغ میکنن. خوندن کد سخت میشه، مدیریت کردن state هاش هم سخت تر میشه، اینجا دقیقا همون جایی هست که useReducer به دردمون میخوره. در واقع انگار شما یه موجود رو میذاری که مسئول هندل کردن تغییرات state ها باشه برای یک کامپوننت. به جای اینکه بیای دونه دونه بگی حالا مقدار لودینگ رو true کن و حالا false کن و دیتا رو بریز و حالا ارور خورد و بیا لودینگ از بین ببر و ارور نشون بده و ...
بهش بگی الان دیتا رو بگیر و اون دیگه خودش همه state ها رو هندل کنه! شما بگی ف اون بره فرحزاد.

خب ببینیم برای انجام اینکار باید چه کار هایی رو انجام بدیم.


آشنا شیم با نحوه کار useReducer

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

اولین مفهوم InitialState هست یا همون state اولیه. این دقیقا مثل اون مقدار دهی اولیه state میمونه که شما توی useState دیدید. پس تعریف کردنش هم خیلی ساده این شکلی میشه:

https://gist.github.com/hesan-aminiloo/b7747d2d3a5653a89bc81a0082671adf

یه Object سادست دیگه درسته؟ دقیقا انگار همون useState ها رو من یکجا نوشتم.

مرحله بعد اینه که یه مامور بسازیم! این مامور وظیفش اینه که بر اساس دستوراتی که بهش میدیم عمل کنه، مثلا بگیم الان دیتا رو دارم میگیرم state ام رو تغییر بده. یا بگیم الان بره متن title رو تغییر بده و خلاصه هر تغییر دیگه ای که روی این state داشتیم رو انجام بده. به این مامور میگیم Reducer.

نوشتنش راحته اما اول بذار بگم چطوری باید بهش دستور بدیم، ببینید دستور دادن به این آقای Reducer یه قاعده خاص داره. مثل سلام نظامی ها باهم! برای همین هر بار که میخوایم با این مامور صحبت کنیم باید توی اون چارچوب باشه.

به این دستور میگیم Action که در واقع یه Object ساده است که دو تا قسمت اصلی داره.

  1. دستور چیه؟ (اجباری)
  2. اطلاعات بیشتری که همراه دستور میخوایم بدیم. (اختیاری)

چند تا نمونه از این دستورا ببینیم:

https://gist.github.com/hesan-aminiloo/98362d776de8bc4451607b60031b0ef8

پس اینا هم یه سری object ساده هستن، بعضیاشون میتونن data داشته باشن، بعضی هم نه.

خب کاری که قراره توی این reducer انجام بدیم اینه:

  1. مقدار اولیه state رو از ورودی بگیریم.
  2. اکشن رو هم از ورودی بگیریم
  3. بر اساس اینکه چه دستوری ارسال شده باید این state رو تغییر بدیم و یه state جدید درست کنیم.

خب با این توضیحات کد این Reducer این شکلی میشه:

https://gist.github.com/hesan-aminiloo/5dfa8e2a44970a17ef202bba2a17056e

خب همینطور که میبینید یه دونه switch داریم که بر اساس دستوری که میاد state رو تغییر میده و میفرسته بره.

الان که با همه این مفاهیم آشنا شدیم، یه دونه فایل میسازیم reducer.js و همه اینا رو اونجا میذاریم:

https://gist.github.com/hesan-aminiloo/3a8a8656eda5b9a6a2da757059a40b3a

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



استفاده از useReducer

بعد اینکه فایل reducer مون رو ایجاد کردیم، حالا وقتشه که بریم توی کامپوننت مون و ازش استفاده کنیم. اولین کار اینه که import بشه پس بالای فایل بنویسید:

import { useReducer } from 'react'; import { initialState, reducer } from './reducer';

خب خیلی سوسکی اون چیزایی که توی فایل reducer بود هم import کردیم ?. داخل کامپوننت این شکلی ازش استفاده میکنیم:

const [state, dispatch] = useReducer(InitialState, reducer);

مقدار state که مشخصه، dispatch هم یه تابعه برای اینکه اکشن ها رو بدیم به reducer که کارشو انجام بده.

حالا دیگه اون تابع بلندی که داشتیم برای دریافت دیتا ها این شکلی میشه:

https://gist.github.com/hesan-aminiloo/92a9de0025aaa46b4d7c2a3933b260d9

چقدر تمیز تر شد نه؟ زمانی که اون dispatch ها صدا زده میشن هم loading رو ردیف میکنه هم بقیه چیزا رو. حالا اگه بخواید مقدار اون title و description رو تغییر بدید هم کافیه فقط dispatch کنید و مقدارش رو بهش بدید. همون موقع state هم آپدیت میشه.


یه مرحله جلو تر

خب حالا که شما این dispatch رو دارید، حتی میتونید اون رو پاس بدید به یه کامپوننت دیگه و مقدار state رو از اونجا هم تغییر بدید. مثلا چطوره که بدیمش به کامپوننت Modal?

https://gist.github.com/hesan-aminiloo/9b0129ba382757d47e27e7885bebd3c7
حالادیگهاگهداخلModalهمdispatchروصدابزنیم،میتونیمهرکاریدوستداشتیمانجامبدیم،اگهنبودیادتونهچیمیشددیگه؟بایدمثلابهازایهرچیزییهدونهcallbackپاسمیدادیمواونطرفصدامیزدیم.



نکته آخر

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

در آخر مثل همیشه ممنونم و میتونید از طریق لینکدین باهام در ارتباط باشید!



usereducerusestatereactجاواسکریپتحسان
برنامه نویس از جلو
شاید از این پست‌ها خوشتان بیاید