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.

Другие вопросы по тегам