自定义带回调的useState Hook运行不符合预期该如何解决?
带回调的自定义useState闭包问题修复方案
问题根因
该问题本质是React的闭包陷阱导致:
postOrder使用了依赖为空数组的useCallback包裹,仅会在组件首次渲染时生成一次,它的闭包捕获的是首次渲染时的orderId值也就是初始值0。- 你传给
setOrderId的回调函数是在postOrder执行时创建的,归属这个旧闭包,即便自定义Hook中是在state更新后才触发回调,回调内部引用的外部orderId依旧是当初闭包捕获的旧值,和最新状态无关联。 - 你的自定义Hook本身逻辑是正常的,所以回调入参
newOrdId可以拿到正确的最新状态值。
修复方案
方案1:直接使用回调入参(推荐,改造成本最低)
自定义Hook已经将最新state作为入参传给回调函数,直接使用该参数即可,不需要引用外部的orderId变量:
const postOrder = useCallback(()=>{ // 订单提交成功后 setOrderId(ordId, (newOrdId:string)=>{ console.log('newOrdId: '+newOrdId); console.log('orderId: '+newOrdId); }); },[]);
方案2:为useCallback补充正确依赖
如果业务逻辑必须引用外部的orderId变量,将orderId加入postOrder的依赖数组即可,保证每次orderId更新时postOrder都会重新生成,捕获最新的状态值:
const postOrder = useCallback(()=>{ // 订单提交成功后 setOrderId(ordId, (newOrdId:string)=>{ console.log('newOrdId: '+newOrdId); console.log('orderId: '+orderId); }); },[orderId]);
如果postOrder需要作为props传给子组件,可配合React.memo避免不必要的重渲染。
方案3:通过useRef存储最新状态
如果不想修改useCallback的依赖,可在自定义Hook中新增ref同步最新状态,使用时从ref中取值:
修改自定义Hook代码:
import {useState, useEffect, useRef, useCallback} from 'react' export function useStateWithCallback(initialState:any) { const [state, setState] = useState(initialState); const callbackRef:any = useRef(null); // 新增ref同步最新state const stateRef = useRef(initialState); const dispatchAction = useCallback((obj:any, callback:any)=>{ callbackRef.current = callback; setState(obj); },[]); useEffect(()=>{ // 每次state更新同步到ref stateRef.current = state; if(callbackRef.current){ callbackRef.current(state); callbackRef.current = null; } },[state]); // 抛出存储最新状态的ref return [state, dispatchAction, stateRef]; }
业务使用代码:
const [orderId, setOrderId, orderIdRef] = useStateWithCallback(0); const postOrder = useCallback(()=>{ // 订单提交成功后 setOrderId(ordId, (newOrdId:string)=>{ console.log('newOrdId: '+newOrdId); // 从ref中取最新状态值 console.log('orderId: '+orderIdRef.current); }); },[]);
内容的提问来源于stack exchange,提问作者Prince Kumar Sharma
相关产品推荐
相关产品推荐

