For

2023.3.26

React.SetStateAction内でe.currentTargetがNULLになるのは仕様です

対象コード

次のシンプルなコンポーネントで確認します。

tsx_____sample.tsx_____import React from 'react';

export const SamplePage = () => {
  const [myState, setMyState] = React.useState({
    text: '',
    num: 0,
  });

  return (
    <>
      <main className="flex justify-center items-center min-h-100vh rounded-12 p-30">
        <div>
          <p>
            <>myState.text: {myState.text || 'Empty'}</>
          </p>
          <input
            className={'mt-20 p-5 border-2 border-gray-200 rounded-4'}
            onChange={(e) =>
              setMyState((state) => {
                return { ...state, text: e.currentTarget.value };
              })
            }
          />
        </div>
      </main>
    </>
  );
};

export default SamplePage;


生じる問題

上記のコンポーネントで、inputタグに文字を入力してみます。



e.currentTargetがnullだと怒られました。
これは e.currentTargetの仕様 通りの挙動です。
仕様には以下の記述があります。

メモ: イベント処理中だけ event.currentTarget の値は利用可能です。
もし console.log() で event オブジェクトを変数に格納し、コンソールで currentTarget キーを探すと、その値は null となります。
console.log(event.currentTarget) を使ってコンソールで表示するか、 debugger 文を使ってコードの実行を一時停止し、 event.currentTarget の値を表示させる必要があります。


今回のサンプルコードではonChangeのハンドラ内の処理ではありますが、React.SetStateActionという非同期処理内でe.currentTargetを参照しようとしたためすでにnullになっているというのが問題の原因でした。

修正

ということでイベント処理中にe.currentTargetからvalueを取り出しておけば良いということになります。

tsx_____sample.tsx_____import React from 'react';

export const SamplePage = () => {
  const [myState, setMyState] = React.useState({
    text: '',
    num: 0,
  });

  return (
    <>
      <main className="flex justify-center items-center min-h-100vh rounded-12 p-30">
        <div>
          <p>
            <>myState.text: {myState.text || 'Empty'}</>
          </p>
          <input
            className={'mt-20 p-5 border-2 border-gray-200 rounded-4'}
            onChange={(e) => {
              const { value } = e.currentTarget; // <- 追加
              setMyState((state) => {
                return { ...state, text: value }; // <- 更新
              });
            }}
          />
        </div>
      </main>
    </>
  );
};

export default SamplePage;


まとめ

react-hook-formなどで非制御コンポーネントとして扱う場面も多いと思いますが、基本的な仕様の話なので記事にしておきました。