useReducer 和 useState 都是来管理 state 的
useReducer 就是基础版 redux,除了不能共享 state 都差不多
而 useState 本身是基于 useReducer 实现的,是 useReducer 的子集:useState 返回的函数内部封装了一个 dispatch
const [state, dispatch] = useReducer(reducer, initialState);
useReducer 传入两个参数,一个是 reducer,一个是初始值
返回两个元素:state 以及与其配套的 dispatch 方法
官方的计数器实例:
const initialState = { count: 0 };
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>
</>
);
}
也可以不通过直接传值进行初始化,而是惰性初始化,这适用于初始 state 需要通过计算才能获得的情景。
将计算 state 的逻辑提到了 reducer 外部,也方便了类似 reset 这样的 action,直接调用同一个计算函数就好了
function init(initialCount) {
return { count: initialCount };
}
function reducer(state, action) {
switch (action.type) {
...
case 'reset':
return init(action.payload);
...
}
}
// 组件要依靠 props 得知 reducer 的初始值
function Counter({ initialCount }) {
// 传入一个 init 函数,这个函数是计算初始 state 的,作为第三个参数
const [state, dispatch] = useReducer(reducer, initialCount, init);
return (
<>
...
...
>
);
}
适合使用 useReducer 替代 useState 的情况:
除此之外,useReducer 可以给那些会触发深更新的组件做性能优化
因为向子组件传递的是 dispatch 而不是 callback function
如果算出来的 state 没变,React 就不重新渲染了,也不执行副作用
如何判断前后 state 是否相等呢?React 使用 Object.is(value1, value2)函数,引用的是同一个对象就判为相等