چند وقت پیش یه تیکه کد رو گیتهاب دیدم برام خیلی جذاب بود! موضوع این بود که کلا به جای useState داشت از useReducer استفاده می کرد. برام جذاب شد و افتادم دنبالش ببینم چرا باید همچین کاری کنم؟
من چون با context کار نکردم زیاد، فکر کردم که این به درد من نمیخوره چون ایده ام این بود که این برای state management هست و خیلی به کار من نمیاد (که البته هست). ولی وقتی اینو دیدم رفتم داکیومنتش رو خوندم و سورپرایز شدم!
یعنی پشمام! Alternative برای useState؟؟ بعدا متوجه شدم چه کارای خفنی میشه کرد باهاش! تو این مطلب میخوایم ببینیم چجوری باید باهاش کار کنیم.
فلسفه اینه که به جای چندتا useState مختلف میتونیم از یه دونه useReducer برای همش استفاده کنیم. دقیقا مثل کاری که با Redux و مشابه هاش انجام میدیم ولی خیلی خیلی ساده و جمع و جور تر. به جای setState کردن یه اکشن میدیم بهش و میگیم هر کدوم از state ها با صدا شدن اون action باید یه حالتی داشته باشن. این کمک میکنه که تعداد rerender های صفحه کمتر بشه، از طرف دیگه نیازی نباشه که چندجا هی بگیم setState. یه قابلیت خیییییلی باحال دیگه اینکه که اون dispatcher اش رو میتونیم به هر کامپوننتی خواستیم پاس بدیم و اونجا state رو عوض کنیم. یعنی جایگزین پاس دادن callback های متعدد.
خب توی حرف توضیح دادنش سخته، بریم توی کد ببینیم چیکار باید کرد:
فرض کنید داخل صفحه یه modal داریم که میخوایم زمانی که باز شد، یه دیتایی از سرور دریافت بشه و نمایش بده، و یه فرمی هم اونجا هست که میتونه فرم رو پر کنه و دیتاش رو ارسال کنه، از طرفی بعد از اینکه این modal بسته میشه باید همه چیز reset بشه و برگرده به حالت اولش.
ساختار element ها میشه شبیه این:
از نظر ساختاری و کتابخونه ها و ابزار ها برامون اصلا مهم نیست. برای ما مهم اینه که چه state هایی تو این کامپوننت داریم.
جمعش میشه ۶ تا. یعنی ۶ تا useState تو یک کامپوننت. حالا جذاب تر میشه اگه تابعی که دیتا رو میگیره و این state ها رو هم تغییر میده ببینیم:
نگید که ندیدید یا ندارید تو کارتون همچین کدی! همه مون دیدیم و زدیم دیگه! همچنین جایی که useState هاش تعریف شدن:
حالا ممکنه شما خیلی باهوش باشی و بیای بگی که خب میتونستی یه دونه useState بنویسی ولی همش رو تو همون تعریف کنی! خب بله میشد! ولی فرقی نمیکرد بهرحال! دوباره همین آش و همین کاسه بود.
مشکل من با این کد ها اینه که به شدت کامپوننت شون رو شلوغ میکنن. خوندن کد سخت میشه، مدیریت کردن state هاش هم سخت تر میشه، اینجا دقیقا همون جایی هست که useReducer به دردمون میخوره. در واقع انگار شما یه موجود رو میذاری که مسئول هندل کردن تغییرات state ها باشه برای یک کامپوننت. به جای اینکه بیای دونه دونه بگی حالا مقدار لودینگ رو true کن و حالا false کن و دیتا رو بریز و حالا ارور خورد و بیا لودینگ از بین ببر و ارور نشون بده و ...
بهش بگی الان دیتا رو بگیر و اون دیگه خودش همه state ها رو هندل کنه! شما بگی ف اون بره فرحزاد.
خب ببینیم برای انجام اینکار باید چه کار هایی رو انجام بدیم.
برای اینکه با useReducer کار کنیم بهتره چندتا مفهوم رو خیلی کپسولی و ریز یاد بگیریم. من سعی میکنم با مثال طبق همون تیکه کد هایی که بالاتر نشون تون دادم برم جلو.
اولین مفهوم InitialState هست یا همون state اولیه. این دقیقا مثل اون مقدار دهی اولیه state میمونه که شما توی useState دیدید. پس تعریف کردنش هم خیلی ساده این شکلی میشه:
یه Object سادست دیگه درسته؟ دقیقا انگار همون useState ها رو من یکجا نوشتم.
مرحله بعد اینه که یه مامور بسازیم! این مامور وظیفش اینه که بر اساس دستوراتی که بهش میدیم عمل کنه، مثلا بگیم الان دیتا رو دارم میگیرم state ام رو تغییر بده. یا بگیم الان بره متن title رو تغییر بده و خلاصه هر تغییر دیگه ای که روی این state داشتیم رو انجام بده. به این مامور میگیم Reducer.
نوشتنش راحته اما اول بذار بگم چطوری باید بهش دستور بدیم، ببینید دستور دادن به این آقای Reducer یه قاعده خاص داره. مثل سلام نظامی ها باهم! برای همین هر بار که میخوایم با این مامور صحبت کنیم باید توی اون چارچوب باشه.
به این دستور میگیم Action که در واقع یه Object ساده است که دو تا قسمت اصلی داره.
چند تا نمونه از این دستورا ببینیم:
پس اینا هم یه سری object ساده هستن، بعضیاشون میتونن data داشته باشن، بعضی هم نه.
خب کاری که قراره توی این reducer انجام بدیم اینه:
خب با این توضیحات کد این Reducer این شکلی میشه:
خب همینطور که میبینید یه دونه switch داریم که بر اساس دستوری که میاد state رو تغییر میده و میفرسته بره.
الان که با همه این مفاهیم آشنا شدیم، یه دونه فایل میسازیم reducer.js و همه اینا رو اونجا میذاریم:
حالا باید داخل کامپوننت مون ازش استفاده کنیم.
بعد اینکه فایل reducer مون رو ایجاد کردیم، حالا وقتشه که بریم توی کامپوننت مون و ازش استفاده کنیم. اولین کار اینه که import بشه پس بالای فایل بنویسید:
import { useReducer } from 'react'; import { initialState, reducer } from './reducer';
خب خیلی سوسکی اون چیزایی که توی فایل reducer بود هم import کردیم ?. داخل کامپوننت این شکلی ازش استفاده میکنیم:
const [state, dispatch] = useReducer(InitialState, reducer);
مقدار state که مشخصه، dispatch هم یه تابعه برای اینکه اکشن ها رو بدیم به reducer که کارشو انجام بده.
حالا دیگه اون تابع بلندی که داشتیم برای دریافت دیتا ها این شکلی میشه:
چقدر تمیز تر شد نه؟ زمانی که اون dispatch ها صدا زده میشن هم loading رو ردیف میکنه هم بقیه چیزا رو. حالا اگه بخواید مقدار اون title و description رو تغییر بدید هم کافیه فقط dispatch کنید و مقدارش رو بهش بدید. همون موقع state هم آپدیت میشه.
خب حالا که شما این dispatch رو دارید، حتی میتونید اون رو پاس بدید به یه کامپوننت دیگه و مقدار state رو از اونجا هم تغییر بدید. مثلا چطوره که بدیمش به کامپوننت Modal?
توی این مدت اخیر که دارم مطلب مینویسم داخل ویرگول، هدفم اینه که کسی که این مطلب رو میخونه انجامش بده و خودش با چالش هاش رو به رو بشه. برام خیلی مهمه که بتونه از این مطالب بهره مند بشه. برای همین به عمد بعضی قسمت ها کچلی داره و من همه ی کار ها رو توضیح نمیدم، میخوام که خودتون اونو کشف کنید، پس اگر به نظرتون مطلب ناقص هست بدونید که این موضوع عمدیه! خودتون دست به کار بشید و قسمت هایی که ناقصه رو کامل کنید! اگه روی گیت هم گذاشتید لینکش رو تو کامنت ها برام بنویسید. دمتون گرم که میخونید و فیدبک میدید!
در آخر مثل همیشه ممنونم و میتونید از طریق لینکدین باهام در ارتباط باشید!