Диаграммы Recharts не обновляются при использовании setInterval для периодического добавления новых данных - PullRequest
0 голосов
/ 02 августа 2020

Я пытаюсь настроить веб-страницу, которая добавляет счет к линейной диаграмме каждые 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>
)

Согласно приведенному выше коду, новые данные должны добавляться в список каждые две минуты, но диаграмма никогда не показывала никаких изменений.

Когда я подтвердил статус ScoreData с помощью console.log (scoreData.length ), консоль просто показала 0, что означает, что данные не добавляются в список.

Итак, я попробовал другой подход, сделав кнопку Test, которая вручную добавляет новые данные, и она работала нормально; список заполнялся новыми данными каждый раз, когда я его нажимал.

В этом случае, что я могу сделать, чтобы диаграмма периодически получала данные в соответствии с установленным мной временем?

Ответы [ 2 ]

0 голосов
/ 02 августа 2020

Ciao, к сожалению, с перехватчиками реакции вы не можете использовать setInterval так, как вы его используете. Проблема связана с самими крючками. Согласно вашему примеру кода, вы хотите добавлять новые данные в свой scoreData каждую секунду. Итак, вы вызываете useInterval, который запускает run, if (updatedM % 2 == 0), вызывает addNewData, который, наконец, добавляет ценность вашему scoreData.

К сожалению, с хуками вы не уверены, что scoreData является уже обновлен данными, добавленными вами ранее setInterval. Зачем? Потому что хуки асинхронные c!

Как это решить? С другим хуком, в частности кастомным хуком!

Вот рабочий пример. Давайте посмотрим на мой пользовательский хук 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 внутри моего component:

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, которую я использую для запуска / остановки вставки новых vlaues в 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) и кнопку для запуска / остановки выборки данных.

0 голосов
/ 02 августа 2020

Я предполагаю, что 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}])
  }

Кнопка Test работает, потому что она закрывается по-новому при каждом рендеринге.

...