問題を簡単に尋ねるためのこのコードの結果を説明しましょう。
_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()
の両方をサポートするようにすることです。 (コンポーネントがマウント解除されているときのみ「クリーンアップ」をログに記録します)
クリーンアップは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>
複数の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"), [] );
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クリーンアップ関数内。