React hooks - правильный способ очистить тайм-ауты и интервалы - PullRequest
0 голосов
/ 31 октября 2018

Я не понимаю, почему, когда я использую функцию setTimeout, мой компонент реакции запускается в бесконечный console.log. Все работает, но ПК начинают адски лагать. Некоторые люди говорят, что эта функция во время ожидания изменяет мое состояние и этот компонент рендеринга, который устанавливает новый таймер и так далее. Теперь мне нужно понять, как очистить это правильно.

export default function Loading() {
  // if data fetching is slow, after 1 sec i will show some loading animation
  const [showLoading, setShowLoading] = useState(true)
  let timer1 = setTimeout(() => setShowLoading(true), 1000)

  console.log('this message will render  every second')
  return 1
}

Очистить в другой версии кода не помогает:

const [showLoading, setShowLoading] = useState(true)
  let timer1 = setTimeout(() => setShowLoading(true), 1000)
  useEffect(
    () => {
      return () => {
        clearTimeout(timer1)
      }
    },
    [showLoading]
  )

Ответы [ 2 ]

0 голосов
/ 12 ноября 2018

Ваш компьютер зависает, потому что вы, вероятно, забыли передать пустой массив в качестве второго аргумента useEffect и вызывали setState в обратном вызове. Это вызывает бесконечный цикл, потому что useEffect срабатывает при рендеринге.

Вот рабочий способ установки таймера при монтировании и очистки его при размонтировании:

function App() {
  React.useEffect(() => {
    const timer = window.setInterval(() => {
      console.log('1 second has passed');
    }, 1000);
    return () => { // Return callback to run on unmount.
      window.clearInterval(timer);
    };
  }, []); // Pass in empty array to run useEffect only on mount.

  return (
    <div>
      Timer Example
    </div>
  );
}

ReactDOM.render(
  <div>
    <App />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>
0 голосов
/ 31 октября 2018

Функция возврата в useEffect запускается каждый раз, когда useEffect запускается (кроме первого запуска при монтировании компонента). Подумайте об этом, поскольку каждый раз, когда выполняется новое useEffect, старое удаляется.

Это рабочий способ использования и очистки тайм-аутов или интервалов:

export default function Loading() {   
     const [showLoading, setShowLoading] = useState(false)

     useEffect(
        () => {
          let timer1 = setTimeout(() => setShowLoading(true), 1000)

          // this will clear Timeout when component unmont like in willComponentUnmount
          return () => {
            clearTimeout(timer1)
          }
        },
        [] //useEffect will run only one time
           //if you pass a value to array, like this [data] than clearTimeout will run every time this value changes (useEffect re-run)
      )

 return showLoading && <div>I will be visible after ~1000ms</div>
}

Если вам нужно очистить тайм-ауты или интервалы где-то за пределами:

export default function Loading() {   
     const [showLoading, setShowLoading] = useState(false)

     const timerToClearSomewhere = useRef(false) //now you can pass timer to another component

     useEffect(
        () => {
          timerToClearSomewhere.current = setInterval(() => setShowLoading(true), 1000)

          return () => {
            clearInterval(timerToClearSomewhere.current)
          }
        },
        []
      )

  //here we can imitate clear from somewhere else place
  useEffect(() => {
    setTimeout(() => clearInterval(timerToClearSomewhere.current), 15000)
  }, [])

 return showLoading && <div>I will be visible after ~1000ms</div>
}
...