一、先理解什么是函数式更新
在使用 useState 时,更新状态有两种方式:
- 直接更新:直接传入新值(如
setCount(count + 1)) - 函数式更新:传入一个函数,函数的参数是当前最新的状态值,返回新状态(如
setCount(prevCount => prevCount + 1))
二、函数式更新的核心好处
1. 确保使用最新的状态值(解决状态滞后问题)
React 的状态更新是异步批量处理的,当你在事件处理、定时器、异步操作中多次更新状态时,直接更新可能会“捕获”到旧的状态值,导致更新结果不符合预期。
示例:直接更新的问题
jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
// 异步批量更新中,这里的 count 始终是点击时的初始值(比如 0)
setCount(count + 1);
setCount(count + 1);
// 最终 count 只会变成 1,而不是预期的 2
};
return <button onClick={handleClick}>Count: {count}</button>;
}
函数式更新的解决方案
jsx
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
// 函数式更新会拿到上一次更新后的最新值
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
// 最终 count 会变成 2,符合预期
};
return <button onClick={handleClick}>Count: {count}</button>;
}
2. 处理依赖于前一个状态的更新
当新状态完全依赖于旧状态时,函数式更新是更可靠的方式——它显式地声明“新状态基于最新的旧状态计算”,避免因闭包捕获旧值导致的错误。
比如处理数组/对象状态时:
jsx
// 错误:直接更新可能使用旧的 list 引用
setList([...list, newItem]);
// 正确:函数式更新确保拿到最新的 list
setList(prevList => [...prevList, newItem]);
3. 代码语义更清晰
函数式更新明确表达了“状态更新依赖于前一个状态”的逻辑,让代码意图更易读、易维护。
总结
- 核心优势:解决异步更新中“状态滞后”问题,确保使用最新的状态值计算新状态。
- 适用场景:多次连续更新状态、状态更新依赖于旧状态(如计数、数组追加)、异步操作中更新状态。
- 语义清晰:显式声明“基于旧状态更新”,提升代码可读性和可维护性。
简单来说,只要你的新状态依赖于旧状态,就优先使用函数式更新,这是 React 官方推荐的最佳实践。