在React中使用useState
时,如果在setTimeout
回调函数中尝试获取最新的state值,可能会发现获取到的是旧值而非最新更新后的值。
function Example() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1); // 更新state
setTimeout(() => {
console.log(count); // 这里获取的count可能是旧值
}, 1000);
};
return <button onClick={handleClick}>Click me</button>;
}
闭包问题:setTimeout
回调函数捕获的是创建时的count
值,形成一个闭包。React函数组件每次渲染都会创建一个新的作用域,而回调函数保留了对旧作用域的引用。
批量更新机制:React会对state更新进行批处理以提高性能,setState
是异步的,不会立即更新值。
函数组件特性:函数组件中的state在每次渲染时都是独立的常量,setTimeout
回调中的count
是创建回调时的值,不会自动更新。
function Example() {
const [count, setCount] = useState(0);
const countRef = useRef(count);
useEffect(() => {
countRef.current = count; // 每次count变化时更新ref
}, [count]);
const handleClick = () => {
const newCount = count + 1;
setCount(newCount);
setTimeout(() => {
console.log(countRef.current); // 通过ref获取最新值
}, 1000);
};
return <button onClick={handleClick}>Click me</button>;
}
function Example() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prevCount => {
const newCount = prevCount + 1;
setTimeout(() => {
console.log(newCount); // 使用更新后的值
}, 1000);
return newCount;
});
};
return <button onClick={handleClick}>Click me</button>;
}
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
console.log(count); // 这里能获取到最新的count
}, 1000);
return () => clearTimeout(timer);
}, [count]); // 依赖count变化
const handleClick = () => {
setCount(count + 1);
};
return <button onClick={handleClick}>Click me</button>;
}
useRef
方案useEffect
监听state变化记住,React的函数组件在每次渲染时都会重新执行,而闭包会捕获创建时的变量值,这是导致这类问题的根本原因。