بهبود Reducer–های ریداکس به ۳ روش مختلف – به زبان ساده
در این مقاله به بررسی چگونگی بهبود Reducer-های ریداکس به 3 روش مختلف با استفاده از اشیا، کلاسها و برنامهنویسی تابعی میپردازیم. پیشفرض ما این است که شما با «ریداکس» (Redux) (+) و کاری که Reducer-ها انجام میدهند آشنایی دارید. تلاش ما بر این است که Reducer-های ریداکس را سریعتر سازیم و از دریافت هشدار یا خطای «پیچیدگی چرخهای» (cyclomatic complexity) جلوگیری کنیم. این نوع پیچیدگیها ممکن است در چیزهایی مانند SonarQube زمانی که تعداد اکشنها افزایش مییابند رخ دهند.
در ادامه مثالی از گزاره switch میبینید که احتمالاً در 99% مثالهای Redux یا Reducer-ها دیدهاید:
1switch (action.type) {
2 case ShowsAction.REQUEST_SHOW_FINISHED:
3 return {
4 ...state,
5 show: action.payload,
6 };
7 case ShowsAction.REQUEST_EPISODES_FINISHED:
8 return {
9 ...state,
10 episodes: action.payload,
11 };
12 default:
13 return state;
14}
ما قصد داریم این مثال را با استفاده از dictionary بهبود بخشیم.
دیکشنری (جفت کلید-مقدار)
دیکشنری صرفاً یک شیء جاوا اسکریپت است که میتوان مقدار رشتهای را به عنوان کلید به آن اضافه کرده و مقداری به آن انتساب داد.
در ادامه نسخه سادهشدهای از ازآنچه قصد داریم بسازیم را میبینید. توجه کنید که کلید نام type اکشن و مقدار انتسابی تابعی است که حالت کنونی و payload را به عنوان آرگومان میگیرد تا یک حالت جدید بسازد.
1const dictionary = {};
2// Add keys to the dictionary
3dictionary['REQUEST_SHOW_FINISHED'] = (state, payload) => {
4 return {
5 ...state,
6 show: payload,
7 }
8};
9dictionary['REQUEST_EPISODES_FINISHED'] = (state, payload) => {
10 return {
11 ...state,
12 episodes: payload,
13 }
14};
15dictionary['REQUEST_CAST_FINISHED'] = (state, payload) => {
16 return {
17 ...state,
18 actors: payload,
19 }
20};
21// Usage
22const newState = dictionary[action.type](state, action.payload); // Warning: This will break if the action.type is not found
رویکرد تابعی جاوا اسکریپت
مثال دیکشنری را با ایجاد تابع baseReducer که یک initialState به عنوان آرگومان نخست و یک دیکشنری به عنوان آرگومان دوم دریافت میکند، بهبود میبخشیم. خوشبختانه خواندن و درک کد زیر آسان است، چون از ثابت نوع اکشن به عنوان نام تابع استفاده میکنیم.
1export const initialState = {
2 currentShowId: '74',
3 show: null,
4 episodes: [],
5 actors: [],
6};
7
8export const showsReducer = baseReducer(initialState, {
9 [ShowsAction.REQUEST_SHOW_FINISHED](state, action) {
10 return {
11 ...state,
12 show: action.payload,
13 };
14 },
15
16 [ShowsAction.REQUEST_EPISODES_FINISHED](state, action) {
17 return {
18 ...state,
19 episodes: action.payload,
20 };
21 },
22
23 [ShowsAction.REQUEST_CAST_FINISHED](state, action) {
24 return {
25 ...state,
26 actors: action.payload,
27 };
28 },
29});
1export default function baseReducer(initialState, reducerDictionary) {
2 // returns a redux reducing function
3 return (state = initialState, action) => {
4 // if the action type is used for a reducer name then this be a reference to it.
5 const reducer = reducerDictionary[action.type];
6
7 // if the action type "reducer" const is undefined or the action is an error
8 // return the state.
9 if (!reducer || action.error) {
10 return state;
11 }
12
13 // if there is a valid reducer call it with the state and action objects.
14 return reducer(state, action);
15 };
16}
در کد فوق پارامتر reducerDictionary دیکشنری است که ارسال کردهایم. توجه کنید که چگونه از action.type در اینجا استفاده کردهایم. reducer[action.type] برای دریافت تابع Reducer صحیح استفاده میشود.
رویکرد کلاس جاوا اسکریپت
در ادامه مثال دیکشنری را با ایجاد کلاس BaseReducer برای بسط Reducer-های کلاس خود بهبود میبخشیم. در کد زیر به شیوه بسط BaseReducer از سوی ShowsReducer توجه کنید. این وراثت است و بخشی از منطقی کلاس دیگر را تجرید میکند به طوری که Reducer-ها تنها بخش مورد نیاز خود را به دست میآورند.
1export default class ShowsReducer extends BaseReducer {
2 initialState = {
3 currentShowId: '74',
4 show: null,
5 episodes: [],
6 actors: [],
7 };
8
9 [ShowsAction.REQUEST_SHOW_FINISHED](state, action) {
10 return {
11 ...state,
12 show: action.payload,
13 }
14 }
15
16 [ShowsAction.REQUEST_EPISODES_FINISHED](state, action) {
17 return {
18 ...state,
19 episodes: action.payload,
20 }
21 }
22
23 [ShowsAction.REQUEST_CAST_FINISHED](state, action) {
24 return {
25 ...state,
26 actors: action.payload,
27 }
28 }
29}
1export default class BaseReducer {
2 initialState = {};
3
4 reducer = (state = this.initialState, action) => {
5 const method = this[action.type];
6
7 if (!method || action.error) {
8 return state;
9 }
10
11 return method.call(this, state, action);
12 };
13}
اگر به BaseReducer فوق نگاه کنید موارد زیر را میبینید:
- در خط 2 initialState وجود دارد که وقتی کلاس Reducer این BaseReducer را بسط دهد، باطل میشود.
- در خط 4 متد Reducer وجود دارد که از سوی ریداکس استفاده خواهد شد.
- در خط 5 به متد کلاسی که با action.type تطبیق مییابد دسترسی پیدا میکنیم.
- در خط 7 اگر متد پیدا نشود یا اگر اکشن به صورت خطا باشد، state کنونی بازگشت مییابد.
- خط 11 متد پیدا شده را با آرگومانهای state و action فراخوانی کرده و state تغییر یافته را که ریداکس مورد استفاده قرار خواهد داد بازگشت میدهد.
بدین ترتیب به پایان این مقاله با موضوع بررسی روشهای بهبود Reducer-های ریداکس میرسیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- مجموعه آموزشهای برنامهنویسی
- آموزش JavaScript ES6 (جاوا اسکریپت)
- آموزش جاوا اسکریپت — مجموعه مقالات جامع وبلاگ فرادرس
- راهنمای مقدماتی ریداکس (Redux) — به زبان ساده
==