تنظیم مجدد Stateهای Redux با استفاده از Root Reducer

زمانی که از Redux برای مدیریت Stateها در اپلیکیشن‌ها استفاده می‌کنیم، تنظیم مجدد یا ریست کردن State کاری است که در اکثر مواقع باید انجام دهیم. یک مثال متداول برای این موضوع هنگام خروج کاربران است.

یکی از رویکردهای مرسوم، افزودن یک شرط RESET_APP به تمام Reducerها است. فرض کنید یک Reducer با عنوان users به صورت زیر داریم.

 

const usersDefaultState = [];

const users = (state = usersDefaultState, { type, payload }) => {
  switch (type) {
    case "ADD_USER":
      return [...state, payload];
    default:
      return state;
  }
};

 

 در این حالت باید یک RESET_APP به switch اضافه کنیم تا به مقدار usersDefaultState بازگردیم.

 

const usersDefaultState = []

const users = (state = usersDefaultState, { type, payload }) => {
  switch (type) {
   case "RESET_APP":
     return usersDefaultState;
    case "ADD_USER":
      return [...state, payload];
    default:
      return state;
  }
};

 

این کد برای زمانی که یک یا نهایتا دو Reducer داشته‌ باشیم مشکلی ایجاد نمی‌کند. اما در برنامه‌های واقعی این روش اصلا کاربرد مناسب ندارد. از آنجائی‌ که احتمالا با تعداد زیادی Reducer سروکار خواهیم داشت، این روش باعث نوشتن کد تکراری در هر یک از Reducerها می‌شود.

 

const usersDefaultState = [];
const users = (state = usersDefaultState, { type, payload }) => {
  switch (type) {
   case "RESET_APP":
    return usersDefaultState;
    case "ADD_USER":
      return [...state, payload];
    default:
      return state;
  }
};

const articlesDefaultState = [];
const articles = (state = articlesDefaultState, { type, payload }) => {
  switch (type) {
   case "RESET_APP":
    return articlesDefaultState;
    case "ADD_ARTICLE":
      return [...state, payload];
    default:
      return state;
  }
};

 

توجه کنید که یکی از اصل‌های مهم برای توسعه‌دهندگان، اجتناب از نوشتن کدهای تکراری است.

متمرکز سازی تنظیم مجدد State

روش بهینه‌ای که وجود دارد این است که یک Root Reducer که بالاتر از بقیه‌ی Reducerهای برنامه قرار می‌گیرد، ایجاد کنیم و در آن شرایط بررسی شده و در صورت لزوم کد مورد نظر ما اعمال شود.

برای این کار معمولا از تابع combineReducers برای ایجاد یک Root Reducer واحد برای Redux Store استفاده می‌کنیم.

 

import { combineReducers } from 'redux';

const usersDefaultState = [];
const users = (state = usersDefaultState, { type, payload }) => //...

const articlesDefaultState = [];
const articles = (state = articlesDefaultState, { type, payload }) => //...

const allReducers = combineReducers({
  users,
  articles
});

 

در این حالت، allReducers به عنوان Root Reducer برنامه ما برای آرگومان تابع createStore در نظر می‌گیریم. برای اینکه یک Root Reducer که شامل Root Reducer اصلی برنامه باشد، ایجاد کنیم کافی است تابعی تعریف کنیم که Root Reducer برنامه را فراخوانی می‌کند.

 

const rootReducer = (state, action) => {
  return appReducer(state, action);
}

 

اکنون، Root Reducer به عنوان تابعی عمل می‌کند که در فراخوانی appReducer قرار گرفته‌ است. دقیقا قبل از این فراخوانی می‌توانیم یک عملکرد مشترک اضافه کنیم.

به طوری‌ که قبل از فراخوانی Reducerها اعمال ‌شود و به این ترتیب برای تنظیم مجدد برنامه، می‌توانیم State را به صورت undefined تنظیم کنیم.

 

const rootReducer = (state, action) => {
  if (action.type === 'RESET_APP') {
    state = undefined;
  }

  return appReducer(state, action);
}

 

 برای اینکه بفهمیم این کار چگونه تنظیم مجدد برنامه را انجام می‌دهد، به بررسی تعریف Reducer می‌پردازیم.

 

const users = (state = usersDefaultState, { type, payload }) => //...

 

مشاهده می‌کنید که مقدار پیش‌ فرض برای پارامتر state در این حالت برابر usersDefaultState است. پارامتر پیش‌ فرض یک تابع فقط و فقط زمانی اعمال می‌شود که مقدار پارامتر دریافتی برابر undefined باشد.

به همین دلیل، مقدار Stateای که تمام Reducerها دریافت می‌کنند همان مقدار پیش‌فرض State خواهد‌بود.

ممکن است این سوال برای شما بوجود آید که در این‌ صورت آیا Mutation در State اتفاق می‌افتد یا نه. باید بگوییم که جواب نه است. چرا که ما تنها رفرنس State را به undefined تغییر می‌دهیم. توجه داشته‌ باشید که Mutation در State برخلاف قوانین Redux است.

تنظیم مجدد به ازای هر Reducer

اعمال کردن تنظیم مجدد متمرکز با استفاده از یک rootReducer مانعی برای داشتن عملکرد‌های دیگر برای RESET_APP بقیه‌ reducerها نیست.

برای مثال، فرض کنید articles یک reducer است که هنگام فعال شدن RESET_APP به جای مقدار پیش‌فرض، یک state را برمی‌گرداند.

 

const articles = (state = articlesDefaultState, { type, payload }) => {
  switch (type) {
    case "RESET_APP":
      return "App is reset";
    case "ADD_ARTICLE":
      return [...state, payload];
    default:
      return state;
  }
};

 

نکته‌ی مهم این است که به طور پیش‌ فرض، تمام Reducerها مقدار پیش‌فرض  stateخود را برمی‌گردانند و تنها زمانی که نیاز داشته‌ باشیم می‌توانیم از تنظیم مجدد استفاده کنیم.

مستثنی کردن Reducerها از تنظیم مجدد

ممکن است بخواهیم از تنظیم مجدد برخی Reducerها اجتناب کنیم. برای این کار می‌توانیم قسمتی از State را که نمی‌خواهیم دچار تنظیم مجدد شود، مشخص کنیم.

برای مثال، اگر بخواهیم از تنظیم مجدد articles اجتناب کنیم کد به این صورت خواهد بود.

 

const rootReducer = (state, action) => {
  if (action.type === 'RESET_APP') {
    const { articles } = state;
    state = { articles };
  }

  return allReducers(state, action);
};

 

اتفاقی که اینجا می‌افتد این است که تابع combineReducers، Reducerها را ادغام کرده و تنها قسمتی از State که مشابه با Reducer مورد نظر است را به آن می‌دهد.

درنتیجه هر Reducer تنها قسمتی از State را که در Scope آن است، دارد.

 

[button class=”github-btn” href=”http://frontcast.ir/course/react-redux”]دوره جامع و پیشرفته React و Redux[/button]

 

دیدگاه‌ها:

امیر

تیر 22, 1400  در  11:09 ق.ظ

سلام وقت بخیر
از appReducer چند جا استفاده شده ولی تعریف خود تابع appReducer جایی توی مقاله اورده نشده !

مسعود صدری

تیر 22, 1400  در  2:32 ب.ظ

سلام
تابع appReducer یک تابع تستی هست که به عنوان مقدار بازگشتی state و action در نظر گرفته شده.

افزودن دیدگاه جدید