پیاده‌سازی Action و Subscription

Implementing Action and Subscription

22 بهمن 1399
پیاده سازی Action و Subscription

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

// Store
const store = createStore(rootReducer);
console.log(store.getState());

حالا می خواهیم به جای console.log کردن store یک Action ساده را برای آن پیاده سازی کنیم:

// Store
const store = createStore(rootReducer);
console.log(store.getState());

// Dispatching Action
store.dispatch();

تابع dispatch یک آرگومان می گیرد که در واقع همان action ما می باشد، یعنی یک شیء جاوا اسکریپتی که دارای خصوصیت type باشد:

store.dispatch({type: 'INC_COUNTER'});

type را دقیقا باید به همین شکل بنویسید. همچنین به صورت قراردادی مقدار type را با حروف بزرگ می نویسند اما شما می توانید از هر رشته دیگری نیز استفاده کنید. در اینجا منظور من از INC_COUNTER عبارت increment counter بوده است (یعنی شمارنده را 1 واحد افزایش بده). همچنین برخی اوقات می خواهیم داده های دیگری را همراه با این شیء ارسال کنیم. به طور مثال INC_COUNTER قرار است شمارنده را 1 واحد افزایش دهد بنابراین نیازی به ذکر مقدار نیست اما اگر یک action دیگر به نام ADD_COUNTER (شمارنده را افزایش بده) داشته باشیم باید تعداد آن را نیز ذکر کنیم:

// Dispatching Action
store.dispatch({type: 'INC_COUNTER'});
store.dispatch({type: 'ADD_COUNTER', value: 10});

من می خواهم این action مقدار شمارنده را 10 واحد افزایش دهد به همین خاطر است که عدد 10 را به آن داده ام. همچنین شما می توانید به جای 10 یا value هم نام دیگر یا عدد دیگری را نیز انتخاب کنید و تنها خصوصیت type است که باید دقیقا به شکل type باشد.

برای نمایش تاثیر این Action ها روی store ما، مقدار زیر را console.log می کنیم:

// Store
const store = createStore(rootReducer);
console.log(store.getState());

// Dispatching Action
store.dispatch({ type: 'INC_COUNTER' });
store.dispatch({ type: 'ADD_COUNTER', value: 10 });
console.log(store.getState());

قبل از آنکه پاسخ را ببینید، سعی کنید خروجی را حدس بزنید. آیا این Action ها state ما را تغییر می دهند؟ در حال حاضر state ما یک خصوصیت counter دارد که مقدار آن صفر است، آیا این مقدار تغییر می کند؟ با توجه به اینکه دو دستور console.log را در کد بالا داریم، اگر دستور node redux-basics.js را در ترمینال اجرا کنید خروجی زیر را می گیرید:

{ counter: 0 }

{ counter: 0 }

ممکن است تصور کنید که خروجی عدد 11 می شود. یعنی action اول 1 واحد و action دوم 10 واحد به counter اضافه کرده باشد و خروجی 11 را به ما بدهد. دلیل تغییر نکردن counter این است که ما هیچ کدی برای دریافت و اجرای کار خاصی با این action ها ننوشته ایم، بلکه فقط یک شیء حاوی اطلاعات را ارسال کرده ایم! اگر یادتان باشد در reducer خود action را به عنوان آرگومان دوم دریافت می کردیم، بنابراین می توانیم آن را تغییر دهیم:

// Reducer
const rootReducer = (state = initialState, action) => {
  if (action.type === 'INC_COUNTER') {
    
  }
  return state;
};

در واقع شرط بالا چک می کند که action.type برابر با INC_COUNTER باشد و اگر اینچنین بود می خواهیم کار خاصی انجام دهیم. روش غلط تغییر state به شکل زیر است:

// Reducer
const rootReducer = (state = initialState, action) => {
  if (action.type === 'INC_COUNTER') {
    state.counter++;
    return state;
  }
  return state;
};

چرا؟ به دلیل اینکه با انجام این کار state به شکل immutable تغییر نمی کند و قبلا توضیح داده بودیم که چرا این کار را نباید انجام دهیم. باید به جای این کار یک شیء جاوا اسکریپتی جدید بسازیم که حاوی state باشد (یعنی از state در یک شیء جدید کپی می گیریم) و سپس خصوصیت مورد نظر خود را در آن override می کنیم:

  if (action.type === 'INC_COUNTER') {
    return {
      ...state,
      counter: state.counter + 1
    }
  }

همیشه و هر جایی که بخواهید state را تغییر دهید باید آن را به شکل immutable تغییر دهید (دقیقا از روش بالا استفاده کنید). من از این شرط یک کپی می گیرم و برای ADD_COUNTER نیز اطلاعات مناسب را اضافه می کنم:

// Reducer
const rootReducer = (state = initialState, action) => {
  if (action.type === 'INC_COUNTER') {
    return {
      ...state,
      counter: state.counter + 1
    }
  }
  if (action.type === 'ADD_COUNTER') {
    return {
      ...state,
      counter: state.counter + action.value
    }
  }
  return state;
};

بنابراین به جای اضافه کردن 1 واحد به ADD_COUNTER مقدار action.value را اضافه کرده ام. دستور return آخر کد نیز برای حالت پیشفرض است (در صورتی که action ما هیچ کدام از دو مورد بالا نباشد). حالا دوباره دستور node redux-basics.js را اجرا کنید تا خروجی زیر را مشاهده نمایید:

{ counter: 0 }

{ counter: 11 }

حالا می بینیم که مقدار counter تغییر کرده است و کد ما به درستی اجرا می شود.

پیاده سازی Subscription

آیا می دانید چه مرحله ای باقی مانده است؟ بله، باید یک subscription نیز بسازیم. کار subscription ها این است که دریافت State را برای کامپوننت های مختلف اتوماتیک کنند تا هر بار مجبور نباشیم getState را فراخوانی کنیم (مثل دستورات console.log بالا). برای انجام این کار یک متد به نام subscribe وجود دارد که یک آرگومان می گیرد: تابعی که با هر بار تغییر state اجرا می شود:

// Subscription
store.subscribe(() => {

});

من در اینجا از توابع ناشناس ES6 استفاده کرده ام اما شما می توانید از هر تابع دیگری نیز استفاده کنید. شما می توانید درون این تابع هر کدی را بنویسید تا زمان تغییر State اجرا شود. مثلا من می گویم:

// Store
const store = createStore(rootReducer);
console.log(store.getState());

// Subscription
store.subscribe(() => {
  console.log('[subscription]', store.getState());
});

// Dispatching Action
store.dispatch({ type: 'INC_COUNTER' });
store.dispatch({ type: 'ADD_COUNTER', value: 10 });
console.log(store.getState());

من فقط دو مورد را console.log کرده ام (یک رشته ساده تا هنگام اجرا آن را تشخیص دهم و getState). من تمام کد بالا را آورده ام تا یک نکته مهم را به شما بگویم: بهتر است همیشه subscription ها را بعد از ساخته شدن Store و قبل از ارسال action ها قرار دهید.

سوال: اگر قرار است باز هم به صورت دستی getState را بنویسیم چرا از subscription استفاده کنیم؟
پاسخ: اگر subscription نداشته باشیم باید خودمان حدس بزنیم که چه زمانی state تغییر می کند که تقریبا کاری غیرممکن است اما با وجود subscription دقیقا می دانیم این اتفاق در چه لحظه ای روی می دهد.

حالا اگر node redux-basics.js را اجرا کنیم، خروجی زیر را می گیریم:

{ counter: 0 }

[subscription] { counter: 1 }

[subscription] { counter: 11 }

{ counter: 11 }

این خروجی روند تغییر state را از لحظه صفر تا تبدیل شدن به 11 نشان می دهد. چرا دو عدد subscription داریم؟ به دلیل اینکه دو action داریم که هر کدام state را تغییر می دهند، بنابراین State دو بار تغییر کرده و ما هم دو پیام subscription دریافت می کنیم. حالا که تمام عناصر پایه ای redux را یاد گرفته ایم چطور می توانیم آن را در React جای بدهیم؟ در قسمت بعد این مورد را بررسی خواهیم کرد.

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

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