React中useEffect无法获取更新后state变量值的问题解决
问题根源:闭包陷阱
你遇到的是典型的React闭包陷阱:你的useEffect依赖数组为空,意味着它只会在组件挂载时执行一次。定时器的回调函数捕获的是组件初始渲染时的bboHistory状态(也就是bp为空数组的版本),之后无论bboHistory怎么更新,定时器里访问的始终是这个初始值——这就是为什么你在useEffect里打印bboHistory.bp一直是空数组,每次请求都传0作为index,导致后端返回重复数据。
而setBBOHistory里的prev参数能拿到最新的状态,所以你的图表能正常更新,因为它直接绑定了最新的bboHistory。
修复方案
推荐用useRef来跟踪最新的状态,因为ref的current属性是可变的,不会受闭包限制:
步骤1:添加useRef跟踪最新状态
在组件内声明一个ref,初始值为bboHistory:
const bboHistoryRef = useRef(bboHistory);
步骤2:同步ref与state更新
用useEffect监听bboHistory的变化,每次状态更新时同步更新ref的current值:
useEffect(() => { bboHistoryRef.current = bboHistory; }, [bboHistory]);
步骤3:在定时器里使用ref获取最新状态
修改useEffect里的定时器回调,用bboHistoryRef.current替代直接访问bboHistory:
useEffect(() => { const interval = setInterval(() => { const currentBP = bboHistoryRef.current.bp; console.log("bb_p", currentBP); requestBBOHistory(currentBP.length); }, 6000); return () => clearInterval(interval); }, []);
完整修改后的代码
import React, {useState, useEffect, useRef} from 'react' import Plot from 'react-plotly.js' function PriceHistory() { const [bboHistory, setBBOHistory] = useState( { bt:[], bp:[], ot:[], op:[] }); const bboHistoryRef = useRef(bboHistory); // 同步ref与state useEffect(() => { bboHistoryRef.current = bboHistory; }, [bboHistory]); async function requestBBOHistory(index) { const response = await fetch(`http://127.0.0.1:5000/bbo-history/${index}`); const data = await response.json(); setBBOHistory(prev => { console.log("prev",prev) console.log("data",data) return { bt: prev.bt.concat(data.bt), bp: prev.bp.concat(data.bp), ot: prev.ot.concat(data.ot), op: prev.op.concat(data.op), }; }); } useEffect(() => { const interval = setInterval(() => { const currentBP = bboHistoryRef.current.bp; console.log("bb_p", currentBP); requestBBOHistory(currentBP.length); }, 6000); return () => clearInterval(interval) }, []); return ( <Plot data={[ { x: bboHistory.bt, y: bboHistory.bp, type: 'scatter' }, { x: bboHistory.ot, y: bboHistory.op, type: 'scatter' } ]} /> ) }
另一种简化思路(可选)
如果你的请求只需要bp的长度,也可以单独用一个useRef跟踪长度,避免同步整个对象:
const bpLengthRef = useRef(0); // 同步长度 useEffect(() => { bpLengthRef.current = bboHistory.bp.length; }, [bboHistory.bp.length]); // 定时器里直接用bpLengthRef.current requestBBOHistory(bpLengthRef.current);
这样更轻量,适合只需要单个属性的场景。
内容的提问来源于stack exchange,提问作者awdreg
相关产品推荐
相关产品推荐

