web-dev-qa-db-ja.com

すべてのレンダリングで「useEffect」のクリーンアップ関数が呼び出されるのはなぜですか?

私はReactを学習していて、useEffectから返された関数はクリーンアップを行うことを意図しており、Reactがコンポーネントがアンマウントされます。

だから私はそれを少し実験しましたが、次の例では、DOMからマウント解除されたときだけではなく、コンポーネントが再レンダリングされるたびに関数が呼び出されることを発見しました。つまり、毎回console.log("unmount");コンポーネントの再レンダリング。

何故ですか?

function Something({ setShow }) {
  const [array, setArray] = useState([]);
  const myRef = useRef(null);

  useEffect(() => {
    const id = setInterval(() => {
      setArray(array.concat("hello"));
    }, 3000);
    myRef.current = id;
    return () => {
      console.log("unmount");
      clearInterval(myRef.current);
    };
  }, [array]);

  const unmount = () => {
    setShow(false);
  };

  return (
    <div>
      {array.map((item, index) => {
        return (
          <p key={index}>
            {Array(index + 1)
              .fill(item)
              .join("")}
          </p>
        );
      })}
      <button onClick={() => unmount()}>close</button>
    </div>
  );
}

function App() {
  const [show, setShow] = useState(true);

  return show ? <Something setShow={setShow} /> : null;
}

ライブの例: https://codesandbox.io/s/vigilant-leavitt-z1jd2

6
Joji

コードを見ると、2番目のパラメーター[array]が原因であると推測できます。あなたはそれを更新しているので、それは再レンダリングを呼び出します。空の配列を設定してみてください。

状態を更新するたびに再レンダリングとアンマウントが呼び出され、その配列は変化します。

0
Ion

React docsには 説明セクション が正確にあります。

要するに、そのような設計は古いデータと更新のバグから保護するためです。

ReactのuseEffectフックは、最初のレンダリングと後続のレンダリングの両方を処理するように設計されています( 詳細はこちら )。


効果は、それらを使用するコンポーネントのライフサイクルではなく、依存関係によって制御されます。

エフェクトの依存関係が変更されると、useEffectは以前のエフェクトをクリーンアップし、新しいエフェクトを実行します。

そのようなデザインはより予測可能です- 各レンダリングには独自の(純粋な)動作効果があります 。これにより、UIに常に正しいデータが表示されるようになります(ReactのメンタルモデルのUIは特定のレンダリングの状態のスクリーンショットであるため)。

私たちが効果を制御する方法は、その依存関係によるものです。

すべてのレンダリングでクリーンアップが実行されないようにするには、エフェクトの依存関係を変更しないでください。

具体的には、arrayが変更されているため、クリーンアップが行われています。つまり、Object.is(oldArray, newArray) === false

useEffect(() => {
  // ...
}, [array]);
//  ^^^^^ you're changing the dependency of the effect

次の行でこの変更を引き起こしています。

useEffect(() => {
  const id = setInterval(() => {
    setArray(array.concat("hello")); // <-- changing the array changes the effect dep
  }, 3000);
  myRef.current = id;

  return () => {
    clearInterval(myRef.current);
  };
}, [array]); // <-- the array is the effect dep
0
nem035

予想通りです。ここのドキュメントに従って、useAffectsは最初のレンダリング、すべての更新およびアンマウント後に呼び出されます。

https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

ヒント

Reactクラスのライフサイクルメソッドに精通している場合は、useEffect Hookを、componentDidMount、componentDidUpdateの前、およびcomponentWillUnmountを組み合わせたものと考えることができます。

0
Mobeen