Hooks:在使用 useReducer 时组合多个 reducer?

IT技术 reactjs react-hooks
2021-04-15 03:14:29

我想在作为第一个参数传递给useReducer. 这是因为我的 reducer 函数依赖于多个switch(首先操作,然后是水果类型)。

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

演示代码(即使 codeandbox 再次关闭):

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

2个回答

您的代码中有一些错误,例如,class而不是className,缺少key属性。我修改了你的样本,看看这里

同样重要的是,reducer 是纯函数——当一个合适的动作触发变化时,总是返回一个新的状态,不要改变以前的状态(和嵌套的属性)。如果没有reducer不能处理动作,就返回之前的状态——不要把reducer扔进去,这也会让它变得不纯。

您的形状的另一种选择是让每个子减速器负责整个状态树的某些子状态,以使其更具可扩展性和可组合性(Link)。所以一个用于苹果的减速器,一个用于香蕉和一个橙子(首先是水果类型,然后是操作)。但原则上,您可以按照自己的意愿/需要处理形状。

希望,这有帮助。


更新:

如果您寻求类似 Redux 的combineReducersfor实现useReducer,也可以看看 Lauri 的回答,尽管我建议在大多数情况下使用不同的实现。在下面的示例中,每个 reducer 只获得它自己的状态部分(“切片”),这降低了它的复杂性。您也可以很好地扩展此解决方案 - 只需添加一个新属性 + 减速器:

// 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>

还没有看过你的沙箱,但这听起来像这个这个帖子。否则,请随意创建一个新问题。
2021-05-28 03:14:29
为什么combinedreducer第一次被调用两次?对正确的结果无害。只是好奇!Codesandbox.io/s/cranky-sanderson-5ewpt
2021-06-21 03:14:29

那些来这里寻找钩子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)

编辑:我最近喜欢减少功能,我认为它的实现方式更干净

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