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

相关推荐
强强学习1 小时前
HTML5 起步
前端·html·html5
念九_ysl3 小时前
前端循环全解析:JS/ES/TS 循环写法与实战示例
前端·javascript·typescript
王亭_6663 小时前
VSCode集成deepseek使用介绍(Visual Studio Code)
ide·vscode·编辑器·deepseek·openrouter
anyup_前端梦工厂5 小时前
了解几个 HTML 标签属性,实现优化页面加载性能
前端·html
前端御书房5 小时前
前端PDF转图片技术调研实战指南:从踩坑到高可用方案的深度解析
前端·javascript
2301_789169546 小时前
angular中使用animation.css实现翻转展示卡片正反两面效果
前端·css·angular.js
爱健身的小范6 小时前
记录一下VScode可以使用nvcc编译,但VS不行的解决方案
ide·vscode·编辑器
翻滚吧键盘6 小时前
vscode复制到下一行
ide·vscode·编辑器
风口上的猪20157 小时前
thingboard告警信息格式美化
java·服务器·前端
程序员黄同学7 小时前
请谈谈 Vue 中的响应式原理,如何实现?
前端·javascript·vue.js