1. ホーム
  2. javascript

[解決済み] useEffectフックにイベントを登録するには?

2022-09-22 08:21:42

質問

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);
  };
}, []);

第二引数に空の配列を指定することで、一度だけ効果を実行させることができます。 componentDidMount . で、その結果を試してみると、キーを打つたびに加算されるのではなく、上書きされるのが不思議です。

私が期待していたのは setUserText( ${userText}${key} ); を使うと、新しく入力されたキーが現在の状態に追加され、新しい状態として設定されますが、その代わりに古い状態を忘れて新しい状態で書き換えられています。

再描画のたびにイベントの登録と解除を行うのは本当に正しい方法だったのでしょうか?

どのように解決するのですか?

このようなシナリオについて最善の方法は、イベント ハンドラで何を行っているかを確認することです。

もしあなたが単に state を設定するだけなら、以前の 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>
  );