React hooks - useReducer

useReducer

用法
  1. 可同时更新多个状态,且能把对状态的修改从组件中独立出来,状态更新逻辑较复杂时可以考虑使用。
  2. 代码逻辑更清晰,代码行为易预测:组件负责发出行为,useReducer 负责更新状态
javascript 复制代码
const [state, dispatch] = useReducer(reducer, initState, initAction?)
  1. reducer=(prevState, action)=> { return '处理好的新状态' } 状态处理函数,接收上一次的旧值,以及dispatch穿入的信息对象,进行一系列处理,返回处理好的新状态(一定要是个崭新的对象,为了组件能够监听到状态的变化)
  2. initState 初始状态
  3. initAction 预处理函数,可选,将初始状态传入进行预处理,返回值被当做初始状态
  4. state 获取状态值
  5. dispatch({type: '此次操作的类型', payload: '提交此次操作需要用到的数据' }) 修改状态值,接收一个信息对象,触发reducer函数的执行,更新 state状态,状态更新导致组件重新渲染
使用
javascript 复制代码
import React from 'react'

// 定义默认值的类型,从默认值身上获取
type UserType = typeof defaultState
// 初始数据
const defaultState = { name: 'liulongbin', age: 16 }
// 状态处理函数
const reducer = (prevState: UserType) => {
  console.log('触发了 reducer 函数')
  return prevState
}
// 预处理函数
const initAction = (initAction: UserType) => {
  return {...initAction , age:Math.round(Manth.abs(initState.age))||18}
}

父组件

javascript 复制代码
export const Father: React.FC = () => {
  const [state, dispatch] = useReducer(reducer, defaultState,initAction)
  return (
    <div>
      <button>修改 name 的值</button>
      <div className="father">
        <Son1 />
        <Son2 />
      </div>
    </div>
  )
}

子组件1

javascript 复制代码
const Son1: React.FC<UserType & {dispatch : React.Dispatch<ActionType>}> = (props) => {
  const {dispatch, ...user} = props
  const changeAge = () => {
    dispatch({type: 'AGE_INCREMENT', payload: 2}) // 年龄一次自增2
  }
  return (
    <div className="son1">
      {JSON.stringify(user)}
      <button onClick={()=>changeAge}>年龄自增2</button>
    </div>
  )
}

子组件2

javascript 复制代码
const Son2: React.FC<UserType & {dispatch : React.Dispatch<ActionType>}> = (props) => {
  const {dispatch, ...user} = props
  const changeAge = () => {
    dispatch({type: 'AGE_DECREMENT', payload: 1}) // 年龄一次自减1
  }
  return (
    <div className="son2">
      {JSON.stringify(user)}
      <button onClick={()=>changeAge}>年龄自减1</button>
      {/* 传第二手dispatch */}
      <GrandSon dispatch={ dispatch } /> 
    </div>
  )
}

孙子组件

javascript 复制代码
const GrandSon: React.FC<{dispatch : React.Dispatch<ActionType>}> = (props) => {
  const { dispatch } = props
  const reset = () => {
    dispatch({type: 'RESET'}) 
  }
  return (
    <div className="grand-son">
      <button onClick={()=>reset}>重置</button>
    </div>
  )
}

样式

css 复制代码
.father {
  display: flex;
  justify-content: space-between;
  width: 100vw;
}

.son1 {
  background-color: orange;
  min-height: 300px;
  flex: 1;
  padding: 10px;
}

.son2 {
  background-color: lightblue;
  min-height: 300px;
  flex: 1;
  padding: 10px;
}
使用 Immer 更简单的编写 reducer

从 use-immer 中导入 useImmerReducer 函数,并替换掉 React 官方的 useReducer 函数的调用

javascript 复制代码
npm install immer use-immer -S

修改 reducer 函数中的业务逻辑,case 代码块中不需要 return 新对象了,可在 prevState 上进行修改

因为 Immer 内部会复制并返回新对象

javascript 复制代码
// 状态处理函数
const reducer = (prevState: UserType, action: ActionType) => {
  console.log('触发了 reducer 函数', action)
  switch (action.type) {
    // 修改名字
    case 'UPDATE_NAME':
      prevState.name = action.payload
      break
    // 年龄自增payload
    case 'INCREMENT':
      prevState.age += action.payload
      break
    // 年龄自减payload
    case 'DECREMENT':
      prevState.age -= action.payload
      break
    // 重置
    case 'RESET':
      return initAction(defaultState)
    default:
      return prevState
  } 
}
javascript 复制代码
export const Father: React.FC = () => {
  // 把 useReducer() 的调用替换成 useImmerReducer()
  const [state, dispatch] = useImmerReducer(reducer, defaultState,initAction)
}
注意事项
  1. 状态被useReducer接管后,不能直接修改 state 的值,因为存储在 useReducer 中的数据都是"不可变"的,要想修改 useReducer 中的数据,必须使用 dispatch 函数 触发 reducer 函数的重新计算。
相关推荐
Aphasia3117 小时前
手写KeepAlive组件
前端·react.js·面试
whatever who cares11 小时前
大型 React 项目的文件结构
前端·react.js·前端框架
假如让我当三天老蒯12 小时前
useCallback 详细解释(从原理到用法)(自学用)
前端·react.js
Vu46112 小时前
nextjs的图片和文字优化
react.js
gogoing15 小时前
React Hooks 完整指南
react.js
假如让我当三天老蒯16 小时前
State和Props区别和左右(自学用)
前端·react.js
夜雪闻竹17 小时前
React Query + REST API 最佳实践
前端·react.js·前端框架
戈德斯文19 小时前
我做了一面互联网摸鱼墙:从无限 Canvas 到本地生产环境
react.js·canvas·next.js
vim怎么退出2 天前
Dive into React——Hooks 原理
react.js·源码阅读