React useEffect перезапуск таймера - PullRequest
0 голосов
/ 09 апреля 2020

Я пробовал ловушку useEffect и столкнулся с проблемой, которую не знаю, как ее исправить.

Я написал простой пример счетчика, который использует setTimeout в useEffect для обновления значений.

        if (countState.stopWatch === 0) {
            dispatch({ type: 'reset' });
        }
        else {
            const timeId = setTimeout(() => {
                console.log("Timeout " + JSON.stringify(countState));
                if (countState.stopWatch > 0) {
                    dispatch({ type: 'decrement_stopwatch' });
                }
            }, 1000);

            return () => {
                clearTimeout(timeId);
            };
        }
    });

Код работает нормально, но есть одна проблема. Когда функция перерисовывается, она вызывает выход из таймера и его повторную настройку, что вызывает пропуски (т. Е. Таймер работает не каждые 1 сек c, а каждые сек c плюс время для повторной визуализации приложения).

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

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

Полный код, включающий решения на основе функций и классов, можно найти здесь:

https://github.com/jmc420/react_examples/blob/master/counter/hooks_counter/src/StopWatch.tsx

Ответы [ 2 ]

0 голосов
/ 10 апреля 2020

Я узнал, что решение. Если useEffect используется без зависимостей, вы получаете предупреждение от реакции, но таймер не завершает работу, когда таймер уменьшается. Затем вы получаете проблему ссылки на устаревшие переменные закрытия, но это исправлено с помощью useRef.

0 голосов
/ 09 апреля 2020

Я пытался изо всех сил пытаться придумать способ использования setTimeout, как вы пытались сделать в функциональном компоненте, но, как вы поняли, есть проблема с крошечным временем между рендерингом и сбросом тайм-аут.

Лучшее, что я мог придумать, это просто использовать setInterval. Если вы не уверены, как использовать это с крючками, я сделал быстрый кодовый ящик с моими экспериментами: https://codesandbox.io/s/rough-shape-rjmjs

Ключ в том, что мы устанавливаем интервал один раз и удаляем только при размонтировании, поэтому он постоянно тикает интервал закрывается по начальному состоянию, например:

setCount(c => {
  // c is the most recent count state
  if (c > 0) {
    return c - 1;
  }
  return c;
});

Если это не соответствует вашим потребностям, я также добавил второй, где он также использует ссылку для обхода проблемы закрытия. Мы просто вызываем функцию, назначенную для ссылки, на каждый интервал, но мы переназначаем, какую функцию выполняет каждый рендеринг.

timerLogicRef.current = () => {
  if (otherCount > 0) {
    setOtherCount(otherCount - 1);
  }
};

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

...