Не ограничиваясь редукторами комбайнов для рефакторинга редукторов.
При работе с приложением React Redux одна из самых важных вещей, которую вы должны сделать, - это избегать написания больших беспорядочных редукторов и смотреть на их рефакторинг на более мелкие управляемые части. Чаще всего это можно сделать с помощью combReducers. Используя объединенные редукторы, вы в основном разделяете свое состояние на несколько тонких фрагментов, каждый из которых управляется отдельным редуктором меньшего размера. Однако, как также упоминалось в документации redux, это, очевидно, не единственный способ разделить логику редуктора.
Давайте возьмем пример состояния матча по крикету, которое выглядит следующим образом
Штат состоит из нескольких иннингов. В каждом иннинге есть данные, такие как биты, боулинг и счет, которые необходимо обновить. В любой момент вы можете обновить состояние для текущего иннинга (скажем, последнего иннинга в массиве).
Поначалу кажется, что наличие независимых редукторов для броска, боулинга и счета - это способ разделить логику и объединить их с помощью combineReducers
. Что-то вроде этого
const rootReducer = combineReducers({ batting: battingReducer, bowling: bowlingReducer, score: scoreReducer });
При этом каждый редуктор может работать с логическим срезом состояния. Например, scoreReducer
работает только с простым фрагментом состояния, например
{ team: 'Team1', runs: 0, wickets: 0, balls: 0 }
Однако общее состояние при объединении редукторов, как указано выше, будет выглядеть следующим образом
{ batting: [...], bowling: [...] score: {...} }
И мы намерены иметь состояние, которое содержит массив из нескольких иннингов, каждый из которых представляет указанное выше состояние.
[ { batting: [...], bowling: [...] score: {...} }, { ... } ]
Введите редукторы более высокого порядка.
Шаблон для этого конкретного приложения заключается в том, что, хотя мы сохраняем массив иннингов, все действия заканчиваются обновлением только текущего иннинга (последнего в массиве).
Мы можем абстрагироваться от возможности выбора последнего элемента списка для обновления до универсального редуктора. Этот редуктор может просто обернуть другой редуктор и передать ему выбранный элемент списка, а не весь список.
Вот пример редуктора высшего порядка lastItemInAListReducer
А затем используйте этот редуктор более высокого порядка в корневом редукторе
const rootReducer = combineReducers({ batting:lastItemInAListReducer(
battingReducer), bowling:lastItemInAListReducer(
bowlingReducer), score:lastItemInAListReducer(
scoreReducer) });
Теперь вы можете использовать ваши редукторы, действующие на срез состояния, но при этом поддерживать массив из нескольких иннингов в глобальном состоянии.
- Обратите внимание, что в этом сценарии у нас будет каждый срез как массив, а не все состояние как массив. Это выглядело бы так:
{ batting: [ [...] ], bowling: [ [...] ], score: [ {...} ] }
Мы можем расширить это, чтобы обрабатывать некоторые сценарии, например, когда начинается новая подача, вы хотите, чтобы в массив был добавлен новый элемент. Редуктор для каждого среза решает, каким будет начальное состояние для этого нового элемента.
Вот пример расширения lastItemInAListReducer
, чтобы знать о действии, которое добавляет новый элемент в массив.
И редуктор комбайна, который необходимо изменить, чтобы передать действие, которое указывает, что новый иннингс должен быть запущен и добавлен в список
const rootReducer = combineReducers({ batting:lastItemInAListReducer(
battingReducer, NEXT_INNING), bowling:lastItemInAListReducer(
bowlingReducer, NEXT_INNING), score:lastItemInAListReducer(
scoreReducer, NEXT_INNING) });
Подводя итог, обратите внимание на шаблоны в своем приложении для создания собственных функций редуктора, которые помогают разделить логику редуктора на более мелкие независимые фрагменты.