使用 setInterval 定期追加新数据时,重新绘制图表未更新

詹姆士

我正在尝试设置一个网页,每 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,这意味着数据没有被附加到列表中。

所以我尝试了一种不同的方法,通过制作一个手动添加新数据的测试按钮,它工作得很好;每次我推送列表时,列表中都充满了新数据。

在这种情况下,我该怎么做才能让图表按照我设置的时间定期接收数据?

乔瓦尼·埃斯波西托

Ciao,不幸的是,对于 React 钩子,你不能setInterval以你使用它的方式使用它。问题与钩子本身有关。根据您的代码示例,您希望scoreData每秒向您添加一个新数据所以你调用useInterval启动run那个,if (updatedM % 2 == 0)调用addNewData最终为你的scoreData.

不幸的是,对于钩子,您不确定scoreData是否已经使用您在 previuos 中添加的数据进行了更新setInterval为什么?因为钩子是异步的!

如何解决这个问题?用另一个钩子,特别是一个定制的钩子!

是一个工作示例。让我们看看我的自定义钩子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返回一个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是一个布尔状态变量,我用来运行/停止向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)和一个按钮来开始/停止数据采样。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章