Сброс состояния Redux с помощью редуктора

Приходилось ли вам когда-нибудь сбрасывать состояние Redux до исходного?

Сброс состояния необходим многим приложениям. Типичный пример ситуации, когда пора сбросить состояние приложения – это выход пользователя из системы.

В этом мануале вы узнаете о централизованном сбросе состояния, логике сброса по редукторам и исключении редукторов из операции сброса.

Требования

Чтобы следовать этому руководству, вам понадобятся базовые навыки работы с React и Redux. Если вы только начинаете работать с Redux, ознакомьтесь с этим мануалом.

Это руководство было проверено с помощью redux v4.1.0.

Ограничения RESET_APP

Самый простой подход к сбросу состояния приложения – добавить условие RESET_APP ко всем редукторам.

Рассмотрим редуктор 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;
  }
};

Этот метод приемлем для одного или двух редукторов, но в любом реальном приложении вам придется повторять один и тот же код для каждого редуктора (а их, скорее всего, будет много):

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;
  }
};

Помните принцип DRY (Don’t Repeat Yourself)? Что ж, этот код полностью противоречит этому принципу, и разработчики должны избегать этого.

Централизованный сброс состояния

Чтобы сосредоточить логику сброса приложения в одном месте, нужно создать корневой редуктор над корневым редуктором приложения. Этот редуктор сможет проверять заданное условие и применять его при необходимости.

Обычно для создания единого корневого редуктора redux нужно использовать функцию combineReducers:

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 – это корневой редуктор вашего приложения, который вы передадите в функцию createStore.

Чтобы создать охватывающий корневой редуктор поверх этого редуктора, нам просто нужно определить функцию, которая вызывает этот корневой редуктор:

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

Прямо сейчас rootReducer действует как функция, стоящая между вызовом allReducers. Прямо перед этим вызовом можно поместить общую функциональность (тогда она будет применяться перед вызовом редукторов). А чтобы сбросить приложение, мы могли бы просто передать состояние undefined:

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

  return allReducers(state, action);
}

Минуточку, но как это сбрасывает приложение? Если внимательно проанализировать определение редуктора:

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

мы увидим, что значением по умолчанию для параметра состояния в этом случае является usersDefaultState, а параметр функции по умолчанию применяется тогда и только тогда, когда полученное значение параметра не определено (даже не null, только undefined). Вот потому все редукторы получат состояние по умолчанию.

Но разве состояние не мутирует при этом? Нет, ведь мы меняем ссылку на состояние на undefined, а не мутируем ее. Помните, что мутация состояния противоречит принципам Redux.

Поредукторный сброс состояния

Применение централизованной логики сброса с помощью rootReducer не мешает вам писать пользовательские функции для RESET_APP в других редукторах.

К примеру, предположим, что при срабатывании RESET_APP редуктор articles должен возвращать состояние, отличное от состояния по умолчанию:

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

Дело в том, что при централизации сброса все редукторы вынуждены возвращаться к своему состоянию по умолчанию; пользовательское поведение сброса можно настроить так, чтобы оно запускалось только тогда, когда оно вам понадобится.

Исключение редукторов из сброса

Может случиться и так, что вы захотите избежать сброса состояния некоторых конкретных редукторов.

Предположим, нам нужно исключить редуктор articles из сброса:

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

  return allReducers(state, action);
};

Итак, что тут происходит? Функция combReducers объединяет редукторы и передает каждому соответствующую часть состояния, так что каждый редуктор имеет в своей области только свою часть состояния.

В этом случае, выбирая часть состояния редуктора articles при помощи state = { articles }, функция allReducers передает state.articles редуктору articles, а state.users – редуктору users. Поскольку state.users имеет значение undefined, а state.articles – нет, будет сброшен только редуктор users.

Заключение

В этой статье вы узнали о централизации сброса состояния, а также изучили логику сброса редуктора на индивидуальной основе и исключения редукторов из сброса.

Помните: следование принципу DRY делает ваш код более понятным и поддерживаемым. Дублирования кода, как правило, можно избежать несколькими способами.

Tags:

Добавить комментарий