私はフックを使用してイベントを登録する方法に関する Udemy コースを受講しており、インストラクターが以下のコードを教えてくれました。
const [userText, setUserText] = useState('');
const handleUserKeyPress = event => {
const { key, keyCode } = event;
if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
setUserText(`${userText}${key}`);
}
};
useEffect(() => {
window.addEventListener('keydown', handleUserKeyPress);
return () => {
window.removeEventListener('keydown', handleUserKeyPress);
};
});
return (
<div>
<h1>Feel free to type!</h1>
<blockquote>{userText}</blockquote>
</div>
);
今ではうまく機能していますが、これが正しい方法であるとは確信していません。その理由は、私の理解が正しければ、再レンダリングのたびにイベントが登録され、登録解除されるので、これが正しい方法だとは思えないからです。
useEffect
そこで、フックを以下のように少し変更しました
useEffect(() => {
window.addEventListener('keydown', handleUserKeyPress);
return () => {
window.removeEventListener('keydown', handleUserKeyPress);
};
}, []);
2 番目の引数として空の配列を指定すると、コンポーネントは効果を 1 回だけ実行し、 を模倣しますcomponentDidMount
。そして、結果を試してみると、入力するすべてのキーで、追加されるのではなく、上書きされるのは奇妙です。
私は期待していましたユーザーテキストを設定する( ${userText}${key}
);新しく入力されたキーを現在の状態に追加して新しい状態として設定しますが、代わりに古い状態を忘れて新しい状態で書き換えます。
再レンダリングのたびにイベントを登録および登録解除するのは本当に正しい方法だったのでしょうか?
ベストアンサー1
このようなシナリオに対処する最善の方法は、イベント ハンドラーで何を実行しているかを確認することです。
state
単にprevious を使って設定するだけならstate
、コールバックパターンを使ってイベントリスナーを登録するのがベストです。イニシャルマウント。
使用しない場合はコールバックパターンリスナー参照とそのレキシカルスコープはイベントリスナーによって使用されていますが、各レンダリングで更新されたクロージャを持つ新しい関数が作成されます。したがって、ハンドラーでは更新された状態にアクセスすることはできません。
const [userText, setUserText] = useState("");
const handleUserKeyPress = useCallback(event => {
const { key, keyCode } = event;
if(keyCode === 32 || (keyCode >= 65 && keyCode <= 90)){
setUserText(prevUserText => `${prevUserText}${key}`);
}
}, []);
useEffect(() => {
window.addEventListener("keydown", handleUserKeyPress);
return () => {
window.removeEventListener("keydown", handleUserKeyPress);
};
}, [handleUserKeyPress]);
return (
<div>
<h1>Feel free to type!</h1>
<blockquote>{userText}</blockquote>
</div>
);