1. ホーム
  2. javascript

[解決済み] Reactフック - タイムアウトとインターバルをクリアする正しい方法

2022-04-26 15:35:59

質問

を使用すると、なぜか setTimeout 関数を実行すると、私のリアクトコンポーネントは無限にコンソール.ログを出力し始めます。全て動いているのですが、PCが地獄のようにラグを始めます。 タイムアウトで状態が変化し、コンポーネントが再レンダリングされ、新しいタイマーが設定される、などと言っている人がいます。今、私はそれが正しいことをクリアする方法を理解する必要があります。

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]
  )

解決方法は?

定義 return () => { /*code/* } 関数内部 useEffect は毎回実行される useEffect が実行されます (コンポーネントマウント時の最初のレンダリングを除く) とコンポーネントアンマウント時 (コンポーネントを表示しなくなった場合) に実行されます。

これは、タイムアウトやインターバルを使用したり、クリアするための作業方法です。

サンドボックスの例 .

import { useState, useEffect } from "react";

const delay = 5;

export default function App() {
  const [show, setShow] = useState(false);

  useEffect(
    () => {
      let timer1 = setTimeout(() => setShow(true), delay * 1000);

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

  return show ? (
    <div>show is true, {delay}seconds passed</div>
  ) : (
    <div>show is false, wait {delay}seconds</div>
  );
}

他のコンポーネントでタイムアウトやインターバルをクリアする必要がある場合。

サンドボックスの例です。

import { useState, useEffect, useRef } from "react";

const delay = 1;

export default function App() {
  const [counter, setCounter] = useState(0);
  const timer = useRef(null); // we can save timer in useRef and pass it to child

  useEffect(() => {
    // useRef value stored in .current property
    timer.current = setInterval(() => setCounter((v) => v + 1), delay * 1000);

    // clear on component unmount
    return () => {
      clearInterval(timer.current);
    };
  }, []);

  return (
    <div>
      <div>Interval is working, counter is: {counter}</div>
      <Child counter={counter} currentTimer={timer.current} />
    </div>
  );
}

function Child({ counter, currentTimer }) {
  // this will clearInterval in parent component after counter gets to 5
  useEffect(() => {
    if (counter < 5) return;

    clearInterval(currentTimer);
  }, [counter, currentTimer]);

  return null;
}

ダン・アブラモフ氏の記事 .