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

Hooks: combine multiple reducers when using useReducer?

我想在作为第一个参数传递给 useReducer 的主减速器中使用嵌套的 reducer 而不是嵌套的 switch 语句(你能做到吗?)这是因为我的reducer函数依赖了不止一个switch(先操作,后水果类型)

我查过 "nested reducers" 但这些问题的解决方案似乎都与 redux 和 combineReducers 有关,其中没有与 Hooks 等效的东西。

Demo code(即使codesandbox再次宕机):

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

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

同样重要的是,reducer pure functions - 当合适的操作触发更改时,总是 return 一个新状态,不要改变以前的状态(和嵌套属性)。如果没有 reducer 不能处理这个动作,只是 return 以前的状态 - 不要扔进 reducer,那也会使它不纯。

您的形状的替代方法是让每个子 reducer 负责整个状态树的某些子状态,以使其更具可扩展性和可组合性 (Link)。所以一个 reducer 用于苹果,一个用于香蕉和一个橙子(首先是水果类型,然后是操作)。但原则上,你可以像你这样处理形状want/need它。

希望对您有所帮助。


更新:

如果您寻求状态的 combineReducers for useReducer, also have a look at Lauri's answer, though I recommend to use a different implementation for most cases. In the following sample each reducer only gets its own part ("slice") 类似 Redux 的实现,这会降低其复杂性。您还可以很好地扩展此解决方案 - 只需添加一个新的 属性 + 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>

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

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)