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)
我想在作为第一个参数传递给 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)