React Hooks之useReducer

useReducer

官网传送门

前言

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

useState 的替代方案。它接收一个形如 (state, action) => newStatereducer,并返回当前的 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: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

reducer 的幂等性

reducer本质是一个纯函数,没有任何UI和反作用。这意味着相同的输入(state、action),reducer函数不管执行多少遍始终会返回相同的输出(newState)。所以经过reducer函数很容易推测state的变化,而且也更加容易单元测试。

state 理解

state是当前应用状态对象,能够理解就是咱们熟知的React里面的state

不少时候state可能会是一个复杂的JavaScript对象,针对这种场景咱们可使用ES6的结构赋值:

// 返回一个 newState (newObject)
    function countReducer(state, action) {
        switch(action.type) {
            case 'add':
                return { ...state, count: state.count + 1; }
            case 'sub':
                return { ...state, count: state.count - 1; }
            default: 
                return count;
        }
    }

关于上面这段代码有两个重要的点须要咱们记住:

  1. reducer处理的state对象必须是immutable(不可变的),这意味着永远不要直接修改参数中的state对象,reducer函数应该每次都返回一个新的state object
  2. 既然reducer要求每次都返回一个新的对象,咱们可使用ES6中的解构赋值方式去建立一个新对象,并复写咱们须要改变的state属性,如上例。

但若是咱们的state是多层嵌套,解构赋值实现就很是复杂:

function bookReducer(state, action) {
        switch(action.type) {
            // 添加一本书
            case 'addBook':
                return {
                    ...state,
                    books: {
                        ...state.books,
                        [bookId]: book,
                    }
                };
            case 'sub':
                // ....
            default: 
                return state;
        }
    }

对于这种复杂state的场景推荐使用immerimmutable库解决。

action 理解

action:用来表示触发的行为。

  • type来表示具体的行为类型
  • payload携带的数据
const action = {
        type: 'addBook',
        payload: {
            book: {
                bookId,
                bookName,
                author,
            }
        }
    }
    function bookReducer(state, action) {
        switch(action.type) {
            // 添加一本书
            case 'addBook':
                const { book } = action.payload;
                return {
                    ...state,
                    books: {
                        ...state.books,
                        [book.bookId]: book,
                    }
                };
            case 'sub':
                // ....
            default: 
                return state;
        }
    }

总结

reducer是一个利用action提供的信息,将statepreState转换到newState的一个纯函数,具备一下几个特色:

  • 语法:(state, action) => newState
  • Immutable:每次都返回一个newState, 永远不要直接修改state对象
  • Action:一个常规的Action对象一般有typepayload(可选)组成
  • type: 本次操做的类型,也是 reducer 条件判断的依据
  • payload: 提供操做附带的数据信息

useState + useContext 实现 redux 数据管理效果

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

import React,{useReducer,useContext} from 'react'

const initailState = {
    a:"11111",
    b:"11111"
}

const reducer = (prevState,action)=>{
    let newstate = {...prevState}
    switch(action.type){
        case "change-a":
            newstate.a = action.value
            return newstate
        case "change-b":
            newstate.b = action.value
            return newstate
        default:
            return prevState
    }
    // return prevState
}

const GlobalContext = React.createContext()
export default function App() {
    const [state, dispatch] = useReducer(reducer, initailState)
    
    return (
        <GlobalContext.Provider value={
            {
                state,
                dispatch
            }
        }>
            <div>
                <Child1/>
                <Child2/>
                <Child3/>
            </div>
        </GlobalContext.Provider>
    )
}

function Child1(){
    const {dispatch} = useContext(GlobalContext)
    return <div style={{background:"red"}}>
        <button onClick={()=>{
            dispatch({
                type:"change-a",
                value:"2222222"
            })
        }}>改变a</button>
        <button onClick={()=>{
            dispatch({
                type:"change-b",
                value:"333333"
            })
        }}>改变b</button>
    </div>
}

function Child2(){
    const {state} = useContext(GlobalContext)
    return <div style={{background:"yellow"}}>
        child2-{state.a}
    </div>
}

function Child3(){
    const {state} = useContext(GlobalContext)
    return <div style={{background:"gray"}}>
        child3-{state.b}
    </div>
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哚啦A孟

谢谢鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值