web-dev-qa-db-ja.com

反応フックuseEffect()のクリーンアップは、componentWillUnmount?

問題を簡単に尋ねるためのこのコードの結果を説明しましょう。

_const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};
_

_ForExample component_がマウントされると、「効果」がログに記録されます。これはcomponentDidMount()に関連しています。

名前の入力を変更すると、「効果」と「クリーンアップ」の両方がログに記録されます。逆に、useEffect()の2番目のパラメーターに_[username]_を追加したため、ユーザー名の入力を変更してもメッセージはログに記録されません。これはcomponentDidUpdate()に関連しています

最後に、_ForExample component_がアンマウントすると、「クリーンアップ」がログに記録されます。これはcomponentWillUnmount()に関連しています。

我々はすべてそれを知っている。

つまり、コンポーネントが再レンダリングされるたびに「クリーンアップ」が呼び出されます(アンマウントを含む)

このコンポーネントがマウント解除された瞬間だけ「クリーンアップ」をログに記録するようにしたい場合は、useEffect()の2番目のパラメーターを_[]_に変更するだけです。

しかし、_[username]_を_[]_に変更すると、_ForExample component_は名前入力用のcomponentDidUpdate()を実装しなくなります。

私がやりたいことは、コンポーネントが名前入力に対してのみcomponentDidUpdate()componentWillUnmount()の両方をサポートするようにすることです。 (コンポーネントがマウント解除されているときのみ「クリーンアップ」をログに記録します)

45
koo

クリーンアップはusernameに依存しないため、2番目の引数として空の配列が指定された別のuseEffectにクリーンアップを配置できます。

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

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

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
42
Tholle

複数のuseEffectを使用できます

たとえば、変数がdata1の場合、これをすべてコンポーネントで使用できます

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
56
Mehran Motiee
function LegoComponent() {

  const [lego, setLegos] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchLegos().then( legos=> {
      if (isSubscribed) {
        setLegos(legos)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {legos.map(lego=> <li>{lego}</li>)}
    </ul>
  )
}

上記のコードでは、fetchLegos関数がpromiseを返します。 useEffectのスコープに条件を設定し、コンポーネントがアンマウントされた後にアプリが状態を設定できないようにすることで、promiseを「キャンセル」できます。

警告:マウントされていないコンポーネントでReact状態の更新を実行できません。これは何もしませんが、アプリケーションのメモリリークを示しています。修正するには、すべてのサブスクリプションと非同期タスクをキャンセルしてくださいuseEffectクリーンアップ関数内。

0
Daniel Yap