React Hooks 学习(喂饭局4) - useReducer

390 阅读3分钟

简介

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

使用方式/语法

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

(1)参数

const [state, dispatch] = useReducer(reducer,initialArg,init);
  1. 参数一:useReducer 接受一个reducer 函数,reducer 接受两个参数一个是 state 另一个是 action 。
  2. 参数二:useReducer接受一个初始state, initialArg。将初始 state 作为第二个参数传入 useReducer 是最简单的方法。
  3. 参数三:useReducer接受一个init函数,通过init(initialArg)来初始化 state 。这样可以惰性地创建初始 state。 由此可知,有两种不同初始化useReducer 的state的方式:一种是直接在第二个参数传入初始state;另一种是在第三个参数通过init()创建初始state

(2)返回值

返回一个状态state和 dispath方法函数,state 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的。

举一个栗子 加减计数器(两个参数)

import React ,{useReducer} from 'react';
const App2 = () => {
    const initialState = { name:'张三' , num : 100 , count : 0 }
    const reducer = ( state , action ) => {
        switch(action.type){
            case 'add':
                return{
                    ...state,
                    count:state.count + 1
                }
            case 'minus':
                return{
                    ...state,
                    count:state.count - 1
                }
            case 'reset':
                return state
            default : 
                throw new Error()
        }
        
        
    }
    const [state, dispatch] = useReducer(reducer, initialState)
    return (
        console.log('aa',state),
        <div>
            <div>
                <button onClick={()=>dispatch({type:'add'})}>加号</button>
            </div>
                现在的值:{state.count}
            <div>
                <button onClick={()=>dispatch({type:'minus'})}>减号</button>
            </div>
        </div>
    );
};
export default App2;

举第二个栗子 ---- 加减计数器(三个参数)

import React ,{useReducer}from 'react';

const App2 = () => {
    const initialState = { name:'张三' , location : '北京' , count : 0 }
    const init = (v) => {
        console.log('v2',Object.prototype.toString.call(v)==='[object Object]') //判断是否是对象
        console.log('v',v)
        return v
    }
    const reducer = ( state , action ) => {

        switch(action.type){
            case 'add':
                return {
                    ...state,
                    count : state.count + 1
                }
            case 'minus':
                return {
                    ...state,
                    count:state.count - 1
                }
                
            case 'reset':
                return init(action.payLoad)
            default : 
                throw Error
        }
        
        
    }
    const [state, dispatch] = useReducer(reducer, initialState , init)
    return (
        console.log('state',state),
        <div>
            <div>
                <button onClick={()=>dispatch({type:'add'})}>加号</button>
            </div>
                现在的值:{state.count}
            <div>
                <button onClick={()=>dispatch({type:'minus'})}>减号</button>
            </div>
            <div>
                <button onClick={()=>dispatch({type:'reset', payLoad : initialState})}>重置</button>
            </div>
        </div>
    );
};

export default App2;

效果图:

a3311239d2c7e0fa5e525f95251b2448.png

向下传递dispatch(官网优化建议)

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

大部分人并不喜欢在组件树的每一层手动传递回调。尽管这种写法更明确,但这给人感觉像错综复杂的管道工程一样麻烦。

在大型的组件树中,我们推荐的替代方案是通过 context 用 useReducer 往下传一个 dispatch 函数:

const TodosDispatch = React.createContext(null);
//TodosApp为上级组件
function TodosApp() {
  // 提示:`dispatch` 不会在重新渲染之间变化
  const [todos, dispatch] = useReducer(todosReducer);

  return (
    <TodosDispatch.Provider value={dispatch}>
      <DeepTree todos={todos} />
    </TodosDispatch.Provider>
  );
}

TodosApp 内部组件树里的任何子节点都可以使用 dispatch 函数向上传递 actions到 TodosApp:

//DeepChild为子组件树中某个组件
function DeepChild(props) {
  // 如果我们想要执行一个 action,我们可以从 context 中获取 dispatch。
  const dispatch = useContext(TodosDispatch);

  function handleClick() {
    dispatch({ type: 'add', text: 'hello' });
  }

  return (
    <button onClick={handleClick}>Add todo</button>
  );
}

总而言之,从维护的角度来这样看更加方便(不用不断转发回调),同时也避免了回调的问题。像这样向下传递 dispatch 是处理深度更新的推荐模式。

参考

React Hooks原理 React官网 老Chen先生 # React学习笔记——Hooks中useReducer的基础介绍和使用