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