React学习笔记——Hooks中useReducer的基础介绍和使用

在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖于类组件来获取数据,处理数据,并向下传递参数给 UI 组件进行渲染。使用 React Hooks 相比于从前的类组件有以下几点好处:

  1. 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护
  2. 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现

Hook 是 React 16.8.0 版本增加的新特性,可以在函数组件中使用 state以及其他的 React 特性
Hooks只能在函数式组件中使用,既无状态组件(所有钩子在用时都要先引入)

1、Hook 使用规则

Hook 就是JavaScript 函数,但是使用它们会有两个额外的规则:
1、只能在函数最外层调用 Hook。不要在循环、条件判断或者嵌套函数(子函数)中调用。
2、只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
3、在多个useState()调用中,渲染之间的调用顺序必须相同

2、useReducer

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

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

(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)返回值

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

(3)实例——加减计数器(两个参数)
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;
(4)实例——加减计数器(三个参数)
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;

效果图:React学习笔记——Hooks中useReducer的基础介绍和使用_第1张图片

3、向下传递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,react.js)