参考
https://zh-hans.reactjs.org/docs/hooks-reference.html#usereducer
一、useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
// reducer 函数
// initialArg state的初始值
// init 惰性地创建初始 state ,特别适合用于有重置需求的场景
useReducer
是 useState
的替代方案。它接收一个形如 (state, action) => newState
的 reducer,并返回当前的 state 以及与其配套的 dispatch
方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
二、使用 useReducer 的场景
在某些场景下,useReducer
会比 useState
更适用,例如:
- state 逻辑较复杂且包含多个子值
- 或者下一个 state 依赖于之前的 state 等。
并且,使用 useReducer
还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch
而不是回调函数 。
三、示例:以下是用 reducer 重写 useState
一节的计数器示例:
const initialState = {count: 0};
// reducer 函数接受两个参数,一个是当前的 state ,另一个是 action: { type, payload }
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: { state.count }
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
)
}
注意
React 会确保 dispatch 函数的标识是稳定的,并且不会在组件重新渲染时改变。这就是为什么可以安全地从 useEffect 或 useCallback 的依赖列表中省略 dispatch。
四、示例2:使用第三个参数,惰性初始化
// const initialState = {count: 0};
function init(initialState) {
return { count: initialState }
}
// reducer 函数接受两个参数,一个是当前的 state ,另一个是 action: { type, payload }
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
// 当操作是 reset 时,根据传入的参数来设定重置的值
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}
// 作为 props 传入
function Counter({ initialState }) {
const [state, dispatch] = useReducer(reducer, initialState, init);
return (
<>
Count: { state.count }
// 所以这里也要传入 payload
<button
onClick={() => dispatch({type: 'reset', payload: initialCount})}>
Reset
</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
)
}