【问题标题】:Redux: Import new reducers and feed them new state on route changeRedux:导入新的 reducer 并在路由更改时为它们提供新的状态
【发布时间】:2017-03-17 21:14:29
【问题描述】:

目标

我想在路由发生变化时更改减速器(并为它们提供新状态)。

当前尝试

我有一个由我的 ComponentPage 减速器处理的 slug (/components/:slug) 设置的路线。这个 reducer 注意到路由发生了变化(通过 Redux-saga),并有机会获取与当前 :slug 相关的状态。

当路由器第一次更新到 /components 页面时,状态看起来像这样:

route: {...}
language: {...}
theme: {...}
componentPage: {
  content: {[default / empty]}
}

ComponentPage 获取与:slug 相关的数据后,如下所示:

route: {...}
language: {...}
theme: {...}
componentPage: {
  content: {...}
  liveExample: {...}
  tabCollection: {...}
}

在上面的状态树中,liveExampletabCollection 等项表示由它们自己的 reducer 管理的新域(并且它们的初始状态由 componentPageReducer 设置)。

我的意图是像这样的项目将由 ComponentPage 基于页面的 :slug 动态设置,以便它们可以被替换为其他组件,而不会在状态树中随处可见可能出现的每个可能的组件组件页面的实例。

问题

我目前设置了配置文件来导入每个页面所需的特定减速器,以便他们可以根据 :slug 将它们提供给 ComponentPage。不幸的是,我当前的实现方法仅能处理初始渲染——在传递之后,导入的 reducer 被它们返回的对象替换。

我目前正在交给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(当componentPageReduceraction.type 不匹配时,它仍会尝试手动调用这些动态加载的reducer,如下所示)。 (它们也接受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
      })
  }
}

可能的解决方案

所以,据我所知,如果我将 reducer 函数本身存储在 state 中并将它们的 return 值映射到不同的键上,这样它们就不会替换自己,可能会起作用,但是... 我好像走上了一条奇怪的路

我认为我目前正在拼凑一些可能不应该被视为适当的 Reducer 组合的东西。我不知道是否有更优雅的方式来处理这种情况。

【问题讨论】:

    标签: redux react-boilerplate


    【解决方案1】:

    动态 reducer 切片的标准方法是保留所有现有切片 reducer 的对象,当您需要在运行时添加一个新对象时,更新该对象并重新运行`combineReducers.给你一个基本的想法:

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

    我的React/Redux links listRedux Techniques#Reducers 部分有几篇关于该主题的文章。您可能还想查看 React-Boilerplate 工具包中的 injectAsyncReducer 实用程序。

    【讨论】:

      猜你喜欢
      • 2016-10-21
      • 2020-09-05
      • 2019-07-28
      • 2018-08-04
      • 1970-01-01
      • 2016-11-26
      • 2021-01-08
      • 1970-01-01
      • 2017-11-27
      相关资源
      最近更新 更多