ReactJS Крючки: тайм-аут при самостоятельном вызове не обновляется - PullRequest
0 голосов
/ 02 апреля 2020

В ReactJS Функциональном Компоненте с использованием Крюков Я хотел бы вызвать функцию, которая будет:

  1. Увеличить свойство в Компоненте Состояние
  2. Установите Timeout для повторного вызова себя через постоянное время

Вот упрощенный пример этого компонента:

const Counter = () => {
  const [count, setCount] = useState(0);
  const [counterTimeout, setCounterTimeout] = useState(null);
  const updateCount = () => {
    setCount(count + 1);
    console.log(count);
    setCounterTimeout(window.setTimeout(updateCount, 500));
  };
  useEffect(() => {
    updateCount();
    return window.clearTimeout(counterTimeout);
  }, []);
  return null;
};

Что я я борюсь с является почему это свойство состояния не обновляется?

Кроме того, бонусных очков , если вы можете сказать мне, как очистить это Timeout на Компонент размонтировать (уверен, что так не будет);

Спасибо ❤️

Ответы [ 2 ]

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

Я исправил ваш код и постараюсь объяснить, что, надеюсь, правильно. Я также добавил альтернативу, использующую setInterval вместо повторного запуска setTimeout.

https://codesandbox.io/s/serene-ives-502hz

Проблема заключается в повторной визуализации компонента. Каждый раз, когда свойство или объект состояния изменяется, компонент перерисовывается, поэтому все объекты внутри создаются снова. Это также верно для updateCount. Но метод внутри useEffect будет воссоздан только при изменении одного из параметров прослушивания. В вашем случае массив пуст, поэтому он будет запускаться только один раз при монтировании Counter компонента. Так что ссылка на updateCount не такая же, как внутри компонента после рендеринга из-за setCount.

Чтобы обойти эту проблему useRef приходит на помощь. При этом может быть создана переменная "stati c". Так что это не будет воссоздано при повторной визуализации компонента. Таким образом, мы можем сохранить здесь ссылку на обработчик таймера и вызвать clearTimeout. Следующая вещь - это доступ count.

Я не уверен, правильно ли это объяснение: setCount не обновит текущий count, но создаст новый объект. Поэтому после вызова setCount текущее значение count остается прежним. Для этого setCount можно вызвать по-другому, предоставив функцию, которая будет вызываться внутри с текущим значением count. Чтобы иметь возможность вывести console.log, просто добавьте еще один useEffect, который будет вызываться каждый раз, когда count изменяется при прослушивании count.

const Counter = () => {
  const [count, setCount] = useState(0);
  const counterTimeout = useRef(null);

  const updateCount = () => {
    setCount(count => count + 1);
    counterTimeout.current = window.setTimeout(updateCount, 500);
  };

  useEffect(() => {
    console.log("count", count);
  }, [count]);

  useEffect(() => {
    updateCount();
    return () => {
      window.clearTimeout(counterTimeout.current);
    };
  }, []);
  return null;
};

Для завершения здесь мой вариант с тем же подходом но используя setInterval. Из-за этого интервал обработки только внутри useEffect, поэтому ссылка не требуется. Он также имеет небольшую оптимизацию с использованием useCallback для updateCount.

const CounterInterval = () => {
  const [count, setCount] = useState(0);

  const updateCount = useCallback(() => {
    setCount(count => count + 1);
  }, [setCount]);

  useEffect(() => {
    console.log("interval count", count);
  }, [count]);

  useEffect(() => {
    const counterTimeout = window.setInterval(updateCount, 500);
    return () => {
      window.clearInterval(counterTimeout);
    };
  }, []);
  return null;
};
0 голосов
/ 02 апреля 2020

Это должно работать:

const Counter = () => {
  const [count, setCount] = useState(0);

  const updateCount = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    const interval = setInterval(updateCount, 1000);
    return () => {
      clearInterval(interval);
    };
  }, [count]);
  return <span>{count}</span>;
};

https://codesandbox.io/s/react-hooks-counter-demo-520gr

с setTimeout.

const Counter = () => {
  const [count, setCount] = useState(0);

  const updateCount = () => {
    setCount(count + 1);
  };

  useEffect(() => {
    const timeout = setTimeout(updateCount, 1000);
    return () => {
      clearTimeout(timeout);
    };
  }, [count]);
  return <span>{count}</span>;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...