React回调函数无法读取最新useState状态的问题及替代方案
问题原因
你遇到的是闭包陷阱:
- 你的
useEffect依赖是空数组[],只会在组件首次挂载时执行一次。 - 此时
refreshWarehouseCallback作为订阅回调被传入,它会捕获这个挂载时刻的myState快照。 - 后续
myState更新时,组件会重新渲染,但旧的订阅回调仍然保留着对初始myState的引用,自然读不到最新值。 - 并不是
useState和回调不兼容,而是订阅回调的闭包没有捕获到更新后的state。
不使用useRef的解决方案
方案1:将回调移入useEffect,添加myState到依赖
每次myState更新时,取消旧订阅并重新订阅,让新回调捕获最新的state:
export function Schedules({ tsmService, push, pubsub }: Props) { const [myState, setMyState] = useState<any>(initialState); useEffect(() => { service .getWarehouses() .then((warehouses) => getCurrentWarehouseData(warehouses)) .catch(() => catchError()); // 把回调写在useEffect内部,直接捕获当前的myState function refreshWarehouseCallback(eventName: string, content: any) { const { warehouseId } = myState; // 现在能拿到最新值了 switch(eventName) { case pushEvents.ramp.updated: { // 处理逻辑 break; } } } const pushToken = push.subscribe('public/ttt/#'); const pubSubToken = pubsub.subscribe('push:ttt.*', refreshWarehouseCallback); return () => { pubsub.unsubscribe(pubSubToken); push.unsubscribe(pushToken); }; }, [myState]); // 添加myState到依赖,每次state更新重新执行effect // ...其他代码 }
方案2:用useCallback包裹回调,配合useEffect依赖
如果回调需要在多个地方复用,可以用useCallback缓存回调,确保每次state更新时回调能拿到最新值:
export function Schedules({ tsmService, push, pubsub }: Props) { const [myState, setMyState] = useState<any>(initialState); // 用useCallback包裹,依赖myState,每次state更新生成新的回调 const refreshWarehouseCallback = useCallback((eventName: string, content: any) => { const { warehouseId } = myState; // 最新state switch(eventName) { case pushEvents.ramp.updated: { // 处理逻辑 break; } } }, [myState]); useEffect(() => { service .getWarehouses() .then((warehouses) => getCurrentWarehouseData(warehouses)) .catch(() => catchError()); const pushToken = push.subscribe('public/ttt/#'); const pubSubToken = pubsub.subscribe('push:ttt.*', refreshWarehouseCallback); return () => { pubsub.unsubscribe(pubSubToken); push.unsubscribe(pushToken); }; }, [refreshWarehouseCallback]); // 依赖回调,回调更新时重新订阅 // ...其他代码 }
注意事项
- 两种方案都会在
myState更新时重新订阅,要确保你的pubsub和push的取消/重新订阅逻辑没有副作用。 - 如果
myState更新频繁,可能会导致多次订阅/取消,这时可以考虑合并state更新来减少触发次数。
内容的提问来源于stack exchange,提问作者Matexon
相关产品推荐
相关产品推荐

