Redux: импортировать новые редукторы и передавать им новое состояние при изменении маршрута
Цель
Я хочу изменить редукторы (и скормить им новое состояние) при изменении маршрута.
Текущая попытка
У меня маршрут настроен слизняком (/components/:slug
) это обрабатывается моим ComponentPage
редуктор. Этот редуктор замечает, что маршрут изменился (через Redux-сагу) и имеет возможность получить состояние, соответствующее текущему :slug
,
Когда маршрутизатор впервые обновляется, чтобы перейти к /components
страница, состояние выглядит примерно так:
route: {...}
language: {...}
theme: {...}
componentPage: {
content: {[default / empty]}
}
После ComponentPage
извлекает данные, связанные с :slug
это выглядит примерно так:
route: {...}
language: {...}
theme: {...}
componentPage: {
content: {...}
liveExample: {...}
tabCollection: {...}
}
В приведенном выше дереве состояний такие элементы, как liveExample
а также tabCollection
представляют новые домены, которые управляются их собственными редукторами (и их начальное состояние устанавливается componentPageReducer
).
Мое намерение состоит в том, что такие элементы будут установлены динамически ComponentPage
на основе страницы :slug
так что их можно заменить на другие компоненты, не засоряя дерево состояний всеми возможными компонентами, которые могут присутствовать на каждом возможном экземпляре страницы компонента.
Проблема
В настоящее время у меня есть файлы конфигурации, настроенные для импорта определенных редукторов, которые нужны каждой странице, чтобы они могли передавать их ComponentPage
на основе :slug
, К сожалению, моего текущего метода реализации достаточно, чтобы позаботиться только о начальном рендеринге - после этого сквозного импорта импортированные редукторы заменяются возвращаемыми объектами.
Пример объекта, который я сейчас передаю ComponentPage
pageConfiguration = {
content: {...},
liveExample: (state, action) => liveExampleReducer(state || liveExampleConfiguration, action),
tabCollection: (state, action) => tabCollectionReducer(state || tabCollectionConfiguration, action),
}
Примечание о том, как я пришел к этому:
Redux-х combineReducers
вернул бы что-то из этой структуры:
liveExample: liveExampleReducer(liveExampleConfiguration, action),
tabCollection: tabCollectionReducer(tabCollectionConfiguration, action),
Это было бы идеально (если бы я только знал, как перечислить их в качестве правильных редукторов). Вместо этого мне пришлось сделать функцию вызываемой, чтобы я мог передать action
(когда componentPageReducer
не соответствует action.type
, он все еще пытается вызвать эти динамически загруженные редукторы вручную, как вы увидите ниже). (Они также принимают state || configuration
так что когда они вызываются в следующий раз, они не продолжают повторную инициализацию с конфигурационными / начальными данными).
ComponentPage / reducer.js
export default function componentPageReducer(state = initialState, action) {
switch (action.type) {
case FETCH_SLUG:
// gets the `pageConfiguration` object from above,
// and generates the state by manually mapping through
// its functions and calling them
if (pages.has(action.payload.slug)) {
return pages.get(action.payload.slug).map((x) => {
if (typeof x === 'function') {
return x(undefined, action)
}
return x
})
}
return state
default:
// an attempt at calling the now-nonexistent
// imported reducer functions
return state.map((x) => {
if (typeof x === 'function') {
return x(state, action)
}
return x
})
}
}
Возможные решения
Итак, насколько я могу судить, это может сработать, если я сохраню функции редуктора сами по себе и сопоставлю их return
цените другой ключ, чтобы они не заменили себя, но... похоже, я иду по странной дороге здесь.
Я думаю, что в настоящее время я работаю над тем, что, вероятно, не следует считать правильным составом редуктора. Я не знаю, есть ли более элегантный способ справиться с подобным сценарием.
1 ответ
Стандартный подход к слайсам динамического редуктора состоит в том, чтобы обойти объект всех существующих редукторов слайсов, и, когда вам нужно добавить новый во время выполнения, обновите этот объект и повторно запустите "Объединение Reducers". Чтобы дать вам основную идею:
let sliceReducers = {
first : firstReducer,
second : secondReducer
};
const initialRootReducer = combineReducers(sliceReducers);
const store = createStore(initialRootReducer);
// sometime later
sliceReducers = {
...sliceReducers,
third : thirdReducer
}
const newRootReducer = combineReducers(sliceReducers);
store.replaceReducer(newRootReducer);
У меня есть пара статей на эту тему в разделе Redux Techniques#Reducers моего списка ссылок React/Redux. Вы также можете посмотреть на injectAsyncReducer
утилита из набора инструментов React-Boilerplate.