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() 后的返回值。这样传参就可以保证初始化函数不会再次运行。

相关推荐
kyriewen8 小时前
Anthropic 估值逼近万亿美元,Claude Sonnet 5 + Claude Science 一天两连发
前端·ai编程·claude
小徐_23339 小时前
Wot UI 2.2.0 发布:Button 新增 subtle,VideoPreview 预览体验继续增强
前端·微信小程序·uni-app
山河木马10 小时前
矩阵专题3-怎么创建投影矩阵(uProjectionMatrix)
javascript·webgl·计算机图形学
天蓝色的鱼鱼11 小时前
关于 CSS 你可能不知道的属性,但关键时刻很有用
前端·css
泯泷12 小时前
第 2 篇:设计第一套字节码:Opcode、Instruction 与 Constant Pool
前端·javascript·安全
妙码生花12 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
泯泷12 小时前
第 1 篇:从 1 + 2 开始:亲手写出第一台 JSVM
前端·javascript·安全
团团崽_七分甜12 小时前
Spring Boot 核心知识点总结
前端
lichenyang45312 小时前
从一个按钮开始,理解 ASCF 框架到底在做什么
前端