大家好,我是前端西瓜哥。今天我们继续 React Hooks 的学习,说一下 useReducer。
useReducer 是一种用于维护状态的 Hook,是在 useState 基础上的封装加强。
useReducer 的用法
useReducer 的常用用法是接受一个 reducer 函数,以及一个状态的初始值。状态的初始值会传给 state,reducer 函数则被组装进 dispatch 方法中,控制各种复杂的状态更新。
const [state, dispatch] = useReducer(reducer, { count: 0 });
reducer 函数这个概念来自 Redux,一个状态管理容器库。它接收当前状态值 state 和行为类型 action,来决定返回哪个值作为最新状态。
我们看看下面的例子:
function reducer(state, action) { switch (action.type) { case 'inc': return { count: state.count + 1 }; case 'dec': return { count: state.count - 1 }; case 'reset': return { count: 0 }; default: throw new Error('未能找到匹配类型'); } }
可以看到,类型不同,会触发不同的分支逻辑。比如当 action.type 为 'inc'(增加的意思)时,我们会将原本的 state.count 加一,然后返回这个新的 state。这个新的 state 会被 dispatch 拿去更新为最新的状态。
下面我们看一个完整的计数器例子,为了表示状态的复杂性,我们用了对象的形式,直接用数字类型其实也可以。
// 注:reducer 在前文的代码块中声明。 function Counter() { const [state, dispatch] = useReducer(reducer, { count: 0 }); return ( <div> <div>count { state.count }</div> <button onClick={ () => dispatch({ type: 'inc' }) }>+1</button> <button onClick={ () => dispatch({ type: 'dec' }) }>-1</button> <button onClick={ () => dispatch({ type: 'reset' }) }>重置</button> </div> ) }
一个 dispatch 配合上 action.type,我们就能有多种操作状态的方式。
useReducer 还可以通过传入第三个参数——初始化函数 init,来实现惰性初始化。
const [state, dispatch] = useReducer(reducer, 0, val => ({ count: val }));
组件首次渲染时,第二个参数会被传入到初始化函数中执行,然后拿执行完的返回值作为状态的初始值。
useReducer 的优点
useReducer 的优点是:
- 通过内聚的方式,解决复杂 state 状态更新逻辑的代码维护问题,比如一些有很多属性的对象。
- 传递给子组件也方便些,不用传多个改变状态的函数,传一个 dispatch 就够了。
- dispatch 函数在每次渲染仍保持引用不变,可以配合 React.memo() 进行子组件渲染优化。
对于更新状态简单的场景,就没有必要使用 useReducer。
结尾
某种意义上,你可以认为 useReducer 是 useState 应用了 Redux 哲学的魔改版,目的是优雅地进行复杂状态的管理。