React——useReducer

一、定义

它允许你向组件里面添加一个reducer

复制代码
const [state, dispatch] = useReducer(reducer, initialArg, init?)

参数

reducer:用于更新 state 的纯函数。参数为 state 和 action,返回值是更新后的 state

initialArg:用于初始化state的任意值。初始值的计算逻辑取决于接下来的init参数

init:用于计算初始值的函数。如果存在,使用 init(initialArg) 的执行结果作为初始值,否则使用 initialArg。

返回值

返回一个由两个值组成的数组

当前的state:初次渲染时,它是init(initialArg)或initialArg

dispatch函数:用于更新state并触发组件的重新渲染。

特点:

  • 这个函数没有返回值
  • 它是为了下一次渲染而更新state,调用该函数后读取state只能获取到调用前的值

注:

1、React 会批量更新 state。state 会在 所有事件函数执行完毕 并且已经调用过它的 set 函数后进行更新,这可以防止在一个事件中多次进行重新渲染。

2、如果在访问 DOM 等极少数情况下需要强制 React 提前更新,可以使用 flushSync。

二、用法

1、向组件添加 reducer

在组件顶层作用域创建一个用于管理状态的reducer

复制代码
import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

为了更新内容,需调用dispatch函数:

复制代码
dispatch({ type: 'incremented_age' });

React会把当前的state和action(这里指:{ type: 'incremented_age' })一起作为参数传给reducer函数,然后reducer函数计算并返回新的state,最后React保存新的state,并使用它渲染组件和更新UI。

2、实现reducer函数

1、往函数体中添加计算并返回新的state的逻辑。

一般写法:使用switch语句通过匹配case条件来计算并返回相应的state

2、dispatch(action)中action可以是任意类型,但他需要携带计算新的state所必须的属性

3、action中的属性(例如上面的type属性)依赖于组件的实际情况。即使它会导致数据的多次更新,每个 action 都只描述一次交互

举个例子:

假设有一个购物车组件,用户可以添加商品。你可能会定义以下 actions:

  • ADD_ITEM:表示用户添加了一件商品。
  • REMOVE_ITEM:表示用户移除了一件商品。

无论添加商品的过程可能涉及多个状态更新(比如更新商品列表、计算总价等),你仍然只会发出一个 ADD_ITEM 的 action。这个 action 清晰地表示了用户的一个操作,而具体的状态更新逻辑则由 reducer 处理。

4、在reducer内部不能修改它,它是只读的,正确的做法是返回新的对象

复制代码
function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
        // 🚩 不要像下面这样修改一个对象类型的 state:
      state.age = state.age + 1;
      return state;
      
      // ✅ 正确的做法是返回新的对象
      return {
        ...state,
        age: state.age + 1
      };

    }

3、避免重复创建初始值

React会保存state的初始值并在下一次渲染时忽略它

类型一:把计算初始值的函数的返回值作为初始值

复制代码
function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, createInitialState(username));

虽然 createInitialState(username) 的返回值只用于初次渲染,但是在每一次渲染的时候都会被调用。如果它创建了比较大的数组或者执行了昂贵的计算就会浪费性能。

类型二:把计算初始值的函数作为第三个参数(更优)

复制代码
function createInitialState(username) {
  // ...
}

function TodoList({ username }) {
  const [state, dispatch] = useReducer(reducer, username, createInitialState);

需要注意的是传入的参数是 createInitialState 这个 函数自身,而不是执行 createInitialState() 后的返回值。这样传参就可以保证初始化函数不会再次运行。

相关推荐
中文呀6 小时前
三维 GIS 离线缓存实战:逆向 CesiumOfflineCache,并亲手为它补上"自动更新"能力
前端
前进前进前进7 小时前
TRAE 全链路实战:做了一个警示牌一键生成器:纯 Canvas、单文件、零依赖
前端
小万7428 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿8 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
OpsEye8 小时前
大模型账单对账难:不同厂商 Token 统计口径差异怎么处理
javascript·ai编程
全栈项目管理程序猿8 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
结局无敌10 小时前
React Native for OpenHarmony 实战:三方库 react-native-modal-dialog 的鸿蒙化适配指南
深度学习·算法·react native·react.js·harmonyos
拖孩10 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户3600555790010 小时前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
Hilaku10 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员