Давайте сделаем наши редукторы чище и гибче

Если вы читаете это, вы, несомненно, знакомы с общей структурой React reducer - функции, которая принимает состояние, и действия, которое будет проходить через гигантский оператор switch и вернуть новый экземпляр состояния.

Когда я изучал Redux, а затем перешел на useReducer с помощью React Hooks, мне показалось крайне странным, что для редукторы.

Пусть запись покажет…

Я не противник переключения.

"Ждать! Я думал, ты только что сказал прекратить их использовать ?! »
- Ты

Я все время использую операторы switch, но не с редукторами.

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

Конечно, переключатели работают, но должен быть способ лучше. Правильно?

Лучший способ

Я бы предложил карту действий.

Если вы знакомы с объектно-ориентированным программированием, начнем с собственного Map объекта JavaScript, который работает как словарь или хэш-таблица.

Синтаксис для Map - это массив кортежей, который выглядит следующим образом:

const myMap = new Map([
  [key1, value1],
  [key2, value2],
]);

Мы можем использовать это как часть нашей замены для переключателя, задав пары ключ-значение:

Ключ = тип действия
Значение = функция для обработки действия (и возврата нового состояния)

В приведенном ниже примере вы увидите это, начиная со строки № 12 как actionMap:

Мы используем карту действий в редукторе, который определяется начиная со строки №7. Во-первых, он попытается получить наш mappedAction из actionMap, используя type в качестве ключа. Если тип соответствует одному из ключей на карте, например actionTypes.BANNER_SHOW, тогда мы выполним функцию сопоставления из пары ключ / значение на нашей карте.

Однако, если кто-то передает действие, которое наш reducer не обрабатывает (то есть его нет в нашей карте действий), будет возвращено текущее состояние. Мы можем думать об этом как о default при использовании метода оператора switch.

Преимущества

Вы можете подумать про себя:

«Это круто, но зачем? Мне, например, нравятся массивные операторы переключения ».
- Вы (возможно)

Вот несколько преимуществ:

Сфера

В отличие от оператора switch, каждая функция имеет свою собственную область действия. Это означает, что вы можете объявить что угодно внутри одной функции и объявить что-то с тем же именем в другой.

Чтобы быть справедливым по отношению к операторам switch, вы МОЖЕТЕ сделать это, если действительно хотите, но вам придется рассматривать свои операторы case как блочные операторы с фигурными скобками, что выглядит очень странный:

switch(something) {
  case 'thing 1': {
    const x = 'some const named x';
    return x;
  }
  case 'thing 2': {
    const x = 'some other const named x';
    return x;
  }
}

Портативная логика

Если бы мы хотели, мы могли бы отделить логику функции от карты и импортировать:

const actionMap = new Map([
  [actionTypes.BANNER_DISMISS, dismiss],   
  [actionTypes.BANNER_SHOW, show,
]);

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

switch(type) {
  case actionTypes.BANNER_DISMISS:
    return dismiss(state);
  case actionTypes.BANNER_SHOW: 
    return show(state, payload);
}

Возможность тестирования

Мы можем имитировать любую или все функции, которые есть на карте, если нам нужно или мы хотим. Мы не можем имитировать переключатель. Фактически, мы можем смоделировать всю карту, если захотим!

Объединение карт

Мы можем легко комбинировать это с другими картами действий.

Если мы используем Redux, это не так уж важно, потому что Redux предоставляет combineReducers функцию util, которая объединяет наши функции reducer. вместе. Однако, если вы используете React Hooks, это чрезвычайно полезно, поскольку позволяет разбить ваш редуктор на несколько меньших редукторов и легко объединить их в один:

const actionMap = new Map([
  ...actionMap1,
  ...actionMap2,
  ...actionMap3,
  ...actionMap4,
]);

Что мы узнали?

Операторы переключения, очевидно, работают, но если вы вообще похожи на меня, вам всегда казалось, что это странный и несколько грязный паттерн. Мы можем сделать наш код более гибким и тестируемым, реализовав карту действий с помощью встроенного объекта JavaScript Map.

Так что вперед и прекратите использовать гигантские переключатели!