React useState值滞后一步,handlePairChange中pairList为空问题排查
问题根因
这是典型的React闭包陷阱问题:
你在异步请求getPairList的成功回调中,给每个返回的pair对象绑定了handlePairChange函数。此时绑定的这个handlePairChange捕获了当时组件渲染的闭包环境,当时pairList还是初始的空数组。后续pairList通过setPairList更新触发组件重渲染,会生成新的handlePairChange函数,但你之前绑在pair对象上的还是旧版的函数,所以每次调用时读到的都是初始的空数组。
最优解决方案
你现在把事件处理函数塞入接口返回的业务数据中本身就不合理,建议将数据和事件逻辑拆分,彻底规避闭包问题:
1. 修改类型定义(PairList.tsx)
删除PairProps中的setPair属性,给PairListProps新增点击回调:
export interface PairListProps { trigger: boolean setTrigger: React.Dispatch<React.SetStateAction<boolean>> pairs: PairProps[] onPairChange: (pair: PairProps) => void // 新增回调 } export interface PairProps { id: string tokenA: Token tokenB: Token exchangeRate: number // 删掉setPair属性 }
2. 修改父组件逻辑(parent.tsx)
删除给pair绑定setPair的逻辑,把handlePairChange作为props传给PairList:
const [listTrigger, setListTrigger] = useState<boolean>(false) const [currPair, setCurrPair] = useState<PairProps>(intialPair) const [pairList, setPairList] = useState<PairProps[]>([]) // 异步请求逻辑简化,不用再绑定setPair function handleListTrigger(e: React.MouseEvent) { const client: Client = new Client() client.getPairList().then(result => { const pairs = result[1].pools setPairList(pairs) }) } React.useEffect(() => { if(pairList.length !== 0) { setListTrigger(true) } }, [pairList]) // 修改回调,直接接收选中的pair,不用遍历 function handlePairChange(selectedPair: PairProps) { console.log("SET CURRENT PAIR TO", selectedPair) setCurrPair(selectedPair) } // 给PairList传对应参数 return( <PairList trigger={listTrigger} setTrigger={setListTrigger} pairs={pairList} onPairChange={handlePairChange} /> )
3. 修改PairList组件逻辑
直接在渲染时绑定点击事件:
export default function PairList({ trigger, setTrigger, pairs, onPairChange }: PairListProps) { if (trigger === true) { return ( <div className="pairlist"> <div className="pairlist-inner"> <button className="close-btn" onClick={() => setTrigger(false)}> Close </button> <table> <tbody> {pairs.map(pair => ( <tr key={pair.id} onClick={() => onPairChange(pair)}> <button>{pair.tokenA.name}/{pair.tokenB.name}</button> </tr> ))} </tbody> </table> </div> </div> ) } return <></> } // 可以删掉单独的Pairs组件了
临时修复方案(不改动现有结构的情况)
如果你不想改动现有结构,可以用useRef存储pairList的最新值,绕过闭包限制:
// 父组件新增ref const pairListRef = useRef<PairProps[]>([]) // 监听pairList更新ref React.useEffect(() => { pairListRef.current = pairList if(pairList.length !== 0) { setListTrigger(true) } }, [pairList]) // handlePairChange里读ref的值 function handlePairChange(e: React.MouseEvent) { const selectedPairID = e.currentTarget.getAttribute("id") const latestPairList = pairListRef.current for (let pair of latestPairList) { if (pair.id === selectedPairID) { setCurrPair(pair) } } }
内容的提问来源于stack exchange,提问作者facetiousfactorial
相关产品推荐
相关产品推荐

