使用 setInterval 定期附加新数据时,Recharts 图表不会更新
Recharts chart not updating when using setInterval to periodically append new data
我正在尝试设置一个网页,每 2 分钟将一个分数添加到折线图上,但它似乎不起作用。
我使用的图表是Recharts的折线图,scoreData的初始值为空列表,{time}的初始形式为{h:0, m:0, s:0}.
我正在使用上下文和挂钩,这样即使用户出于某种原因临时访问我将创建的其他页面,页面也不会停止计数。
我写下的代码如下;
const Timer = () => {
const {
scoreData,
setScoreData,
time,
setTime,
interv,
setInterv,
} = useContext(BaseContext);
const addNewData = () => {
setScoreData([...score, {Some New data}])
}
const start = () => {
run();
setInterv(setInterval(run, 1000));
};
var updatedS = time.s,
updatedM = time.m,
updatedH = time.h;
const run = () => {
updatedS++;
if (updatedS === 60) {
updatedM++;
updatedS = 0;
if (updatedM % 2 == 0) {
addNewData();
console.log(scoreData.length);
}
}
if (updatedM === 60) {
updatedH++;
updatedM = 0;
}
return setTime({
s: updatedS,
m: updatedM,
h: updatedH,
});
};
return (
<div>
// The code of the line chart goes in here.
<button onClick={start}>Start</button>
<button onClick={addNewData}>Test Button</button>
</div>
)
根据上面的代码,每两分钟应该向列表添加一个新数据,但是图表没有显示任何变化。
当我使用 console.log(scoreData.length) 确认 scoreData 的状态时,控制台只显示 0,这意味着数据没有被附加到列表中。
所以我尝试了一种不同的方法,制作了一个手动添加新数据的测试按钮,并且效果很好;每次我推送列表时,列表都会充满新数据。
在这种情况下,如何让图表按照我设置的时间定时接收数据?
我假设 setScoreData([...score, {Some New data}])
应该是 setScoreData([...scoreData, {Some New data}])
你的 setInterval
收到一个固定 scoreData
的闭包并继续调用它。所以 setScoreData([...scoreData, {Some New data}])
总是设置相同的数据。
在你的情况下,最好用 useRef hook:
来解决这个问题
const {
scoreData,
setScoreData,
time,
setTime,
interv,
setInterv,
} = useContext(BaseContext);
const scoreDataRef = useRef(scoreData);
scoreDataRef.current = scoreData;
const addNewData = () => {
setScoreData([...scoreDataRef.current, {Some New data}])
}
测试按钮可以工作,因为它在每次渲染时都有一个新的闭包。
再见,不幸的是,对于 React Hooks,您不能以您使用它的方式使用 setInterval
。问题与钩子本身有关。根据您的代码示例,您希望每秒向 scoreData
添加一个新数据。因此,您调用 useInterval
启动 run
,if (updatedM % 2 == 0)
调用 addNewData
,最终为您的 scoreData
.
增加价值
不幸的是,使用挂钩您不确定 scoreData
是否已使用您在之前 setInterval
中添加的数据更新。为什么?因为钩子是异步的!
如何解决这个问题?使用另一个挂钩,特别是自定义挂钩!
Here 一个工作示例。
让我们看看我的自定义钩子 useInterval
:
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
正如您在我的自定义挂钩中看到的那样,我使用 2 useEffect
: 1 来记住最新的 useInterval
回调(使用 useRef
);还有一个是用来设置延迟的setInterval
(这个最后一个useEffect
returns一个clearInterval
取消最后一个setInterval
)。
然后我可以在我的组件中调用 useInterval
:
useInterval(() => {
if (sampling) {
let result = _.cloneDeep(list1);
result.push({
x: Math.floor(Math.random() * 100),
y: Math.floor(Math.random() * 100)
});
console.log(result);
setlist1(result);
}
}, 1000);
list1
是我的值数组(你的 scoreData
),sampling
是一个 bool 状态变量,我用它来 run/stop 将新值插入 list1
.
最后,我的 return
:
return (
<div>
<ScatterChart
width={600}
height={400}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid />
<XAxis type="number" dataKey={"x"} />
<YAxis type="number" dataKey={"y"} />
<Tooltip cursor={{ strokeDasharray: "3 3" }} />
<Legend />
<Scatter
name="values"
data={list1}
fill="#8884d8"
line
shape="circle"
/>
</ScatterChart>
<button onClick={start}>
{sampling ? "Stop sampling" : "Start sampling"}
</button>
</div>
);
我使用了 ScatterChart
(图形上等于 LineChart
)和一个按钮来 start/stop 数据采样。
我正在尝试设置一个网页,每 2 分钟将一个分数添加到折线图上,但它似乎不起作用。
我使用的图表是Recharts的折线图,scoreData的初始值为空列表,{time}的初始形式为{h:0, m:0, s:0}.
我正在使用上下文和挂钩,这样即使用户出于某种原因临时访问我将创建的其他页面,页面也不会停止计数。
我写下的代码如下;
const Timer = () => {
const {
scoreData,
setScoreData,
time,
setTime,
interv,
setInterv,
} = useContext(BaseContext);
const addNewData = () => {
setScoreData([...score, {Some New data}])
}
const start = () => {
run();
setInterv(setInterval(run, 1000));
};
var updatedS = time.s,
updatedM = time.m,
updatedH = time.h;
const run = () => {
updatedS++;
if (updatedS === 60) {
updatedM++;
updatedS = 0;
if (updatedM % 2 == 0) {
addNewData();
console.log(scoreData.length);
}
}
if (updatedM === 60) {
updatedH++;
updatedM = 0;
}
return setTime({
s: updatedS,
m: updatedM,
h: updatedH,
});
};
return (
<div>
// The code of the line chart goes in here.
<button onClick={start}>Start</button>
<button onClick={addNewData}>Test Button</button>
</div>
)
根据上面的代码,每两分钟应该向列表添加一个新数据,但是图表没有显示任何变化。
当我使用 console.log(scoreData.length) 确认 scoreData 的状态时,控制台只显示 0,这意味着数据没有被附加到列表中。
所以我尝试了一种不同的方法,制作了一个手动添加新数据的测试按钮,并且效果很好;每次我推送列表时,列表都会充满新数据。
在这种情况下,如何让图表按照我设置的时间定时接收数据?
我假设 setScoreData([...score, {Some New data}])
应该是 setScoreData([...scoreData, {Some New data}])
你的 setInterval
收到一个固定 scoreData
的闭包并继续调用它。所以 setScoreData([...scoreData, {Some New data}])
总是设置相同的数据。
在你的情况下,最好用 useRef hook:
const {
scoreData,
setScoreData,
time,
setTime,
interv,
setInterv,
} = useContext(BaseContext);
const scoreDataRef = useRef(scoreData);
scoreDataRef.current = scoreData;
const addNewData = () => {
setScoreData([...scoreDataRef.current, {Some New data}])
}
测试按钮可以工作,因为它在每次渲染时都有一个新的闭包。
再见,不幸的是,对于 React Hooks,您不能以您使用它的方式使用 setInterval
。问题与钩子本身有关。根据您的代码示例,您希望每秒向 scoreData
添加一个新数据。因此,您调用 useInterval
启动 run
,if (updatedM % 2 == 0)
调用 addNewData
,最终为您的 scoreData
.
不幸的是,使用挂钩您不确定 scoreData
是否已使用您在之前 setInterval
中添加的数据更新。为什么?因为钩子是异步的!
如何解决这个问题?使用另一个挂钩,特别是自定义挂钩!
Here 一个工作示例。
让我们看看我的自定义钩子 useInterval
:
function useInterval(callback, delay) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
}
正如您在我的自定义挂钩中看到的那样,我使用 2 useEffect
: 1 来记住最新的 useInterval
回调(使用 useRef
);还有一个是用来设置延迟的setInterval
(这个最后一个useEffect
returns一个clearInterval
取消最后一个setInterval
)。
然后我可以在我的组件中调用 useInterval
:
useInterval(() => {
if (sampling) {
let result = _.cloneDeep(list1);
result.push({
x: Math.floor(Math.random() * 100),
y: Math.floor(Math.random() * 100)
});
console.log(result);
setlist1(result);
}
}, 1000);
list1
是我的值数组(你的 scoreData
),sampling
是一个 bool 状态变量,我用它来 run/stop 将新值插入 list1
.
最后,我的 return
:
return (
<div>
<ScatterChart
width={600}
height={400}
margin={{ top: 20, right: 20, bottom: 20, left: 20 }}
>
<CartesianGrid />
<XAxis type="number" dataKey={"x"} />
<YAxis type="number" dataKey={"y"} />
<Tooltip cursor={{ strokeDasharray: "3 3" }} />
<Legend />
<Scatter
name="values"
data={list1}
fill="#8884d8"
line
shape="circle"
/>
</ScatterChart>
<button onClick={start}>
{sampling ? "Stop sampling" : "Start sampling"}
</button>
</div>
);
我使用了 ScatterChart
(图形上等于 LineChart
)和一个按钮来 start/stop 数据采样。