setTimeout Внутри setInterval React - PullRequest
       96

setTimeout Внутри setInterval React

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

Я пытаюсь сделать карусель, реагируя на некоторые анимации. У меня есть некоторое состояние, настроенное для изменения текущего рендеринга дочернего элемента + должны ли появляться переходы затухания.

Я сталкиваюсь с некоторыми проблемами

  1. иногда пользователь меняет страницу и setTimeout внутри next() и previous() будут срабатывать, и я получаю консольную ошибку об изменении состояния после размонтирования компонента.
  2. Пользователь может просто спамить следующую / предыдущую кнопку и всю карусель даст кому-то приступ

Я уже реорганизовал start() и stop(), чтобы использовать intervalRef, и у меня есть ощущение, что преобразование тайм-аутов в timeoutRef решит обе эти проблемы проблемы, но я не уверен, как

type Props = {
  interval?: number;
  timeout?: number;

  children: React.ReactNodeArray;
}
const Carousel: React.FC = ({ interval = 4000, timeout = 500, children }) => {
  const [fade, setFade] = useState(true); // Start fade animation
  const [childIndex, setChildIndex] = useState(0); // Which child to render

  const intervalRef = useRef<NodeJS.Timer | null>(null);

  const next = useCallback(() => {
    setFade(false); // do the fade out animation

    setTimeout(() => {
      setChildIndex((pIndex) => (pIndex + 1) % children.length); // update which to display

      setFade(true); // display fade in animation
    }, timeout);
  }, [timeout, children]);
  const previous = useCallback(() => {
    setFade(false); // do the fade out animation

    setTimeout(() => {
      setChildIndex((pIndex) => ((pIndex - 1) + children.length) % children.length); // update which to display

      setFade(true); // display fade in animation
    }, timeout);
  }, [timeout, children]);

  const start = useCallback(() => {
    if (intervalRef.current === null) {
      intervalRef.current = setInterval(next, interval);
    }
  }, [next, interval]);
  const stop = useCallback(() => {
    if (intervalRef.current !== null) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  }, []);

  useEffect(() => {
    start();

    return () => stop();
  }, [start, stop]);

  return (
    <>
      <Fade in={fade} timeout={timeout} onMouseEnter={stop} onMouseLeave={start}> // don't switch item on hover
        <div>
          {children[childIndex]}
        </div>
      </Fade>
      <Button onClick={previous}>Previous</Button>
      <Button onClick={next}>Next</Button>
    </>
  )
}

1 Ответ

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

setTimeout действительно должно вызываться внутри Effect, чтобы вы могли clearTimeout при отключении компонента.

Что-то вроде этих строк:

useEffect(() => {
  const tId = setTimeout(() => {
     // do stuff
  }, 500);
  return () => clearTimeout(tId);
}, []);

Для решения с помощью «пользователей, разбивающих клавиатуру», посмотрите на debouncing или дросселирование обработчики событий

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...