حالا که با redux آشنا شده ایم، از این قسمت به بعد روی مباحث پیشرفته تر و مباحث تکمیلی redux کار خواهیم کرد. در این قسمت موضوع بحث ما middleware است. اگر یادتان باشد در جلسات اولیه آشنایی با redux تصویر زیر را در اختیارتان گذاشته بودم:
این تصویر جریان کاری redux را به شما نشان می داد و به عبارتی خلاصه تمام redux بود. حالا باید با مفهوم جدیدی به نام middleware آشنا شویم که بین ارسال action و دریافت توسط reducer قرار می گیرد:
توجه داشته باشید که مفهوم middleware هیچ ارتباط خاصی به react ندارد بلکه مربوط به پکیج redux است، چه به برنامه react ما وصل شده باشد و چه وصل نشده باشد. اگر برنامه نویس سمت سرور باشید (مخصوصا اگر با Express.js کار کرده باشید) احتمالا می دانید middleware ها چه چیزی هستند. Middleware به توابع یا کدهایی گفته می شود که توسط شما وارد یک فرآیند خاص در برنامه می شوند و به عنوان بخشی از همان فرآیند اجرا می شوند اما روند اجرای فرآیند را مختل نمی کنند.
بنابراین با نگاه به تصویر بالا متوجه می شویم که در redux می توانیم چیزی به نام middleware داشته باشیم و middleware تمام action های ما را گرفته و قبل از رسیدن به reducer کار خاصی با آن ها انجام می دهد، البته اختلالی در روند اجرای کار ایجاد نخواهد کرد. این کار خاص می تواند به سادگی log کردن اطلاعات باشد اما جنبه اصلی آن ها برای ما، نقش مهم آن ها در اجرای کدهای نامتقارن در redux است!
برای اینکه با سادگی middleware ها آشنا شویم باید آن ها را به پروژه شمارنده خودمان اضافه کنیم تا بفهمید که کار با آن ها چقدر ساده است. همانطور که گفتم middleware ها معمولا یک تابع هستند و شما می توانید از middleware های آماده استفاده کنید اما من می خواهم middleware خودم را ایجاد کنم که کارش log کردن action های مختلف ما است. برای انجام این کار به index.js بروید و مثل من یک تابع تعریف کنید:
const logger = store => { };
نام این تابع به سلیقه شما بستگی دارد و من به خاطر کاری که قرار است انجام دهد نامش را logger گذاشته ام. این تابع ما store را به صورت یک پارامتر دریافت می کند چرا که بعدا با استفاده از یک متد خاص از پکیج redux آن را به middleware متصل می کنیم. این متد به صورت خودکار تابع middleware ما را اجرا خواهد کرد و store را بر می گرداند.
حالا باید درون این تابع، یک تابع دیگر را برگردانید:
const logger = store => { return next => { } };
این تابع دوم پارامتری به نام next دریافت می کند. جالب اینجاست که این تابع نیز یک تابع دیگر را برمی گرداند!
const logger = store => { return next => { return action => { } } };
همانطور که می بینید، تابع سوم action ما را به عنوان پارامتر می گیرد و مثل توابع دیگر به صورت خودکار توسط redux اجرا خواهد شد. حالا می توانیم درون تابع سوم، کدهای مورد نظر خود را بنویسیم تا بین action و reducer اجرا شوند. من گفتم که می خواهم به سادگی action های خودم را log کنم بنابراین:
const logger = store => { return next => { return action => { console.log('[Middleware] Dispatching', action); const result = next(action); console.log('[Middleware] next State', store.getState()); return result; } };
تمام کد بالا یک middleware است. اول از همه action مورد نظر را log کرده ام تا در قسمت کنسول مرورگر دیده شود. سپس تابع next را صدا زده ام و action را به آن پاس داده ام. با این کار آن را درون یک ثابت به نام result قرار داده ایم و در انتها آن را return کرده ایم. این کار به middleware می گوید به سراغ action بعدی برو چرا که کار ما با این یکی تمام شده است. البته قبل از return کردن و رفتن به سراغ Action بعدی، یک بار دیگر دستور log را نوشته ام و متد getState را صدا زده ام. همانطور که گفتم ما از طریق تابع اول (خارجی ترین تابع در کد بالا) به store دسترسی داریم و store نیز متدی به نام getState دارد که state فعلی را به ما بر می گرداند. من این کد را نوشته ام تا وضعیت store را پس از هر action مشاهده کنیم.
یادتان باشد که ما نیازی به صدا زدن هیچ کدام از این توابع نداریم، خود redux آن ها را برایمان اجرا خواهد کرد. بنابراین تنها کاری که باقی مانده است این است که به store بگوییم، این کد به عنوان middleware نوشته شده است. برای انجام این کار در همین فایل index.js باید applyMiddleware را وارد کنید بنابراین دستور قدیمی import را به شکل زیر ویرایش کنید:
import { createStore, combineReducers, applyMiddleware } from 'redux';
حالا در قسمت createStore می توان گفت:
const logger = store => { return next => { return action => { console.log('[Middleware] Dispatching', action); const result = next(action); console.log('[Middleware] next State', store.getState()); return result; } }; const store = createStore(rootReducer, applyMiddleware(logger));
به همین راحتی middleware را به store داده ایم. همچنین اگر چندین middleware داشته باشید می توانید تک تک آن ها را به applyMiddleware بدهید تا به ترتیب اجرا شوند. مثلا:
const store = createStore(rootReducer, applyMiddleware(logger, checker, director));
کد بالا را به شکل قبلی برگردانید و به مرورگر بروید تا کدها را تست کنیم. حالا اگر در مرورگر روی دکمه Increment کلیک کنیم، در قسمت کنسول شاهد چنین چیزی هستیم:
مورد اول همان action ما است که خودمان INCREMENT را برایش تعیین کرده بودیم و مورد دوم state فعلی ما پس از فشردن دکمه است. همانطور که می بینید شمارنده روی عدد 1 قرار گرفته است. هر بار که روی یکی از این دکمه ها کلیک کنید و action ای ارسال شود، middleware ما نیز اجرا خواهد شد:
البته معمولا کسی از middleware برای log کردن وضعیت برنامه استفاده نمی کند و کار اصلی آن ها مواردی مثل اجرای کدهای نامتقارن است که در قسمت های بعدی در موردشان صحبت خواهیم کرد. با این حال اطلاع داشتن از وضعیت store به ما کمک زیادی در debug کردن و کد نویسی راحت تر می کند بنابراین خیلی خوب می شد که روشی برای انجام این کار وجود داشت که علاوه بر نمایش وضعیت در هنگام ارسال action، در لحظه به لحظه (حتی بدون action ها) وضعیت برنامه را به ما نشان دهد. در قسمت بعد در این مورد نیز صحبت خواهیم کرد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.