定期的に新しいデータを追加するためにsetIntervalを使用すると、チャートを更新しない

2020-08-02 javascript reactjs recharts

2分ごとにスコアを折れ線グラフに追加するWebページをセットアップしようとしていますが、機能していないようです。

私が使用しているグラフは、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>
)

上記のコードによると、2分ごとに新しいデータをリストに追加する必要がありますが、グラフには変更が表示されていません。

console.log(scoreData.length)を使用してscoreDataのステータスを確認したところ、コンソールに0が表示されました。これは、データがリストに追加されていないことを意味します。

そこで、新しいデータを手動で追加するテストボタンを作成して別の方法を試しましたが、うまくいきました。リストは、プッシュするたびに新しいデータでいっぱいになりました。

この場合、設定した時間に従って定期的にチャートがデータを受信するようにするにはどうすればよいですか?

Answers

setScoreData([...score, {Some New data}])setScoreData([...scoreData, {Some New data}])必要があるとsetScoreData([...scoreData, {Some New data}])

setIntervalは、 scoreDataが固定されたクロージャーを受け取り、それを呼び出し続けます。そのため、 setScoreData([...scoreData, {Some New data}])常に同じデータを設定します。 あなたの場合、 useRefフックでこの問題を修正する方が良いです:

  const {
    scoreData,
    setScoreData,
    time,
    setTime,
    interv,
    setInterv,
  } = useContext(BaseContext);
  const scoreDataRef = useRef(scoreData);
  scoreDataRef.current = scoreData;

  const addNewData = () => {
    setScoreData([...scoreDataRef.current, {Some New data}])
  }

レンダリングごとに新しいクロージャーを取得するため、テストボタンは機能します。

Ciao、残念ながら、reactフックでは、 setInterval使用方法は使用できません。問題はフック自体に関連しています。コード例によれば、 scoreData新しいデータをscoreDataに追加するとします。したがって、 useIntervalrun useIntervalを呼び出し、 if (updatedM % 2 == 0)場合、最終的に値をscoreDataに追加するaddNewDataを呼び出します。

残念ながら、フックを使用すると、previuos setInterval追加したデータですでにscoreDataが更新されているかどうかはscoreDataません。どうして?フックは非同期だからです!

これを解決するには?別のフック、特にカスタムフック付き!

ここで実際の例です。 私のカスタムフックuseInterval見て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を使用)。もう1つはsetIntervalの遅延を設定するために使用されsetInterval (この最後のuseEffectは、最後のsetIntervalをキャンセルするために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への新しいvlauesの挿入を実行/停止するために使用するブール状態変数です。

最後に、私の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と同じ)とボタンを使用して、データのサンプリングを開始/停止しました。

Related