【问题标题】:Hooks: combine multiple reducers when using useReducer?Hooks:使用 useReducer 时组合多个减速器?
【发布时间】:2019-12-09 07:33:28
【问题描述】:

我想在作为第一个参数传递给useReducer 的主reducer 中使用嵌套reducer 而不是嵌套switch 语句(你甚至可以这样做吗?)。这是因为我的reducer函数依赖不止一个switch(先操作,再水果类型)。

我查找了“嵌套减速器”,但这些问题的解决方案似乎都与 redux 和 combineReducers 相关联,其中没有与 Hooks 等效的。

Demo code(即使codeandbox再次关闭):

它实际上并没有显示在代码沙箱中(因为沙箱本身无法正常工作),但在我自己的机器上,单击“添加”按钮后,我得到了Uncaught TypeError: fruits.apples.map is not a function。但是,在此之前,map 工作正常,所有项目都按预期呈现。

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    那些来这里寻找combineReducers()函数的useReducer()钩子,这可能对你有帮助

    const combineReducers = (...reducers: Function[]) => 
      (state: any = initialState, action: any): any => {
        for(let i=0;i<reducers.length;i++) 
          state = reducers[i](state, action)
        return state;
      }
    

    把它当作

    // At module level, so the combined function doesn't change.
    const combinedReducer = combineReducers(reducer1, reducer2);
    
    // Inside your component.
    const [state, dispatch] = useReducer(combinedReducer, initialState)
    

    编辑:我最近对 ​​reduce 函数点了一个赞,我认为它的实现方式更简洁

    const combineReducers = (...reducers) => 
      (state, action) => 
        reducers.reduce((newState, reducer) =>
             reducer(newState, action), state)
    

    【讨论】:

      【解决方案2】:

      您的代码中有一些漏洞,例如 class 而不是 className,缺少 key 属性。我修改了你的示例,看看here

      reducer 是pure functions 也很重要 - 当适当的操作触发更改时总是返回一个新状态,不要改变以前的状态(和嵌套属性)。如果不是,reducer 无法处理该动作,只需返回之前的状态 - 不要放入 reducer,这也会使其不纯。

      您的形状的替代方法是让每个子减速器负责整个状态树的某些子状态,以使其更具可扩展性和可组合性 (Link)。因此,一个用于苹果的减速器,一个用于香蕉的减速器,一个用于橙子的减速器(首先是水果类型,然后是操作)。但原则上,您可以根据需要/需要处理形状。

      希望,这会有所帮助。


      更新:

      如果您为useReducer 寻找类似Redux 的combineReducers 实现,也可以看看Lauri 的回答,不过我建议在大多数情况下使用不同的实现。在以下示例中,每个 reducer 仅获取其状态的own part ("slice"),这降低了其复杂性。您还可以很好地扩展此解决方案 - 只需添加一个新属性 + 减速器:

      // combine reducers ala Redux: each can handle its own slice
      const combineReducers = slices => (prevState, action) =>
        // I like to use array.reduce, you can also just write a for..in loop 
        Object.keys(slices).reduce(
          (nextState, nextProp) => ({
            ...nextState,
            [nextProp]: slices[nextProp](prevState[nextProp], action)
          }),
          prevState
        );
      
      // atomar reducers only get either apple or orange state
      const apples = (state, action) => action.type === "ADD_APPLE" ? state + 1 : state;
      const oranges = (state, action) => action.type === "ADD_ORANGE" ? state + 1 : state;
      
      const App = () => {
        const [state, dispatch] = React.useReducer(
          combineReducers({ apples, oranges }), // here we create the reducer slices
          { apples: 0, oranges: 0 }
        );
        const handleAddApple = () => dispatch({ type: "ADD_APPLE" });
        const handleAddOrange = () => dispatch({ type: "ADD_ORANGE" });
      
        return (
          <div>
            <p>Apples: {state.apples}, Oranges: {state.oranges}</p>
            <button onClick={handleAddApple}>add Apple</button>
            <button onClick={handleAddOrange}>add Orange</button>
          </div>
        );
      };
      
      ReactDOM.render(<App />, document.getElementById("root"));
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
      <div id="root"></div>

      【讨论】:

      • 为什么combinedreducer第一次被调用了两次?对正确的结果没有损害。只是好奇! codesandbox.io/s/cranky-sanderson-5ewpt
      • 尚未查看您的沙盒,但这听起来像是 thisthis 的帖子。否则,请随意创建一个新问题。
      猜你喜欢
      • 2017-02-26
      • 1970-01-01
      • 1970-01-01
      • 2021-03-08
      • 2016-04-16
      • 2017-09-27
      • 1970-01-01
      • 2016-06-18
      • 1970-01-01
      相关资源
      最近更新 更多