为何useMutation的onSuccess回调输出陈旧的count状态值?
为什么代码输出是0而非666?
这是典型的闭包陷阱导致的问题,具体原因如下:
- 当你点击按钮调用
mutate()时,当前执行的onSuccess回调是组件初始渲染(此时count=0)时创建的,它捕获了这个时刻的count变量。 - 2秒后
setCount(666)触发组件重新渲染,虽然会生成新的useMutation实例和新的onSuccess回调,但已经发起的那个mutation绑定的还是旧的onSuccess,它只会访问闭包里保存的旧值0。 - 你给
useMutation传入的[count]作为键,确实会在count变化时重建mutation实例,但这只影响之后调用的mutate方法,而点击按钮时调用的mutate是count=0时的版本,已经发起的mutation不会受新实例影响。
解决方案:用useRef获取最新状态
useRef的current属性是可变的,不会受组件重新渲染的影响,能始终保存最新的count值:
import "./styles.css"; import { useMutation } from "react-query"; import { useState, useRef, useEffect } from "react"; export default function App() { const [count, setCount] = useState(0); const countRef = useRef(count); // 每次count更新时同步到ref useEffect(() => { countRef.current = count; }, [count]); const { mutate } = useMutation( () => { return new Promise((resolve) => { setTimeout(() => resolve("data"), 5000); }); }, { onSuccess: () => { console.log(countRef.current); // 此时会输出666 } } ); return ( <div className="App"> <button onClick={() => { mutate(); setTimeout(() => { setCount(666); }, 2000); }} > Button </button> </div> ); }
补充说明
如果你尝试把count作为mutation的参数传入,只能拿到调用mutate时的旧值,无法获取之后更新的最新值,所以这种方式不适合你的场景。而useRef是最直接的解决闭包捕获旧值问题的方案。
内容的提问来源于stack exchange,提问作者Priyanshu
相关产品推荐
相关产品推荐

