useReducer使用和原理

640?wx_fmt=png


以下介绍的 Hook,有些是上一节中基础 Hook 的变体,有些则仅在特殊情况下会用到。

useReducer

 
 
const [state, dispatch] = useReducer(	
  reducer,	
  initialArg,	
  init	
);


useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)


在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。


以下是用 reducer 重写 useState 一节的计数器示例:


 
 
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: 'increment'})}>+</button>	
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>	
    </>	
  );	
}

指定初始 state

有两种不同初始化 useReducer state 的方式,你可以根据使用场景选择其中的一种。将初始 state 作为第二个参数传入 useReducer 是最简单的方法:


 
 
const [state, dispatch] = useReducer(	
    reducer,	
    {count: initialCount}	
  );


注意 React 不使用 state = initialState 这一由 Redux 推广开来的参数约定。有时候初始值依赖于 props,因此需要在调用 Hook 时指定。如果你特别喜欢上述的参数约定,可以通过调用 useReducer(reducer, undefined, reducer) 来模拟 Redux 的行为,但我们不鼓励你这么做。

惰性初始化

你可以选择惰性地创建初始 state。为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)


这么做可以将用于计算 state 的逻辑提取到 reducer 外部,这也为将来对重置 state 的 action 做处理提供了便利:


 
 
function init(initialCount) {	
  return {count: initialCount};	
}	
	
function reducer(state, action) {	
  switch (action.type) {	
    case 'increment':	
      return {count: state.count + 1};	
    case 'decrement':	
      return {count: state.count - 1};	
    case 'reset':	
      return init(action.payload);	
    default:	
      throw new Error();	
  }	
}	
	
function Counter({initialCount}) {	
  const [state, dispatch] = useReducer(reducer, initialCount, init);	
  return (	
    <>	
      Count: {state.count}	
      <button	
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>	
	
        Reset	
      </button>	
      <button onClick={() => dispatch({type: 'increment'})}>+</button>	
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>	
    </>	
  );	
}	
	
function render () {	
  ReactDOM.render(<Counter initialCount={0} />, document.getElementById('root'));	
}


我们使用 useReducer 和使用 redux 十分类似,设计思想都是大同小异。


原理

 
 
let memoizedState;	
function useReducer(reducer, initialArg, init) {	
  let initState = void 0;	
  if (typeof init !== 'undefined') {	
    initState = init(initialArg)	
  } else {	
    initState = initialArg	
  }	
  function dispatch(action) {	
    memoizedState = reducer(memoizedState, action)	
    render()	
  }	
  memoizedState = memoizedState || initState	
  return [memoizedState, dispatch]	
}	
	
function useState(initState) {	
  return useReducer((oldState, newState) => newState, initState)	
}

useReducer 是 useState 的内部实现


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值