ドロップダウンメニューを実装してeventオブジェクトに非同期でアクセスする際にエラーが出た。


今回、Reactでドロップダウンメニューを実装していて、詰まってしまった部分になります。

何が起こったか

上記のように、ドロップダウンメニューを作成して、テストするか!というときにエラーが起きました。

1回目は選択できるのですが、2回目の選択でエラーになってしまう・・・。

書いたコード

今回、以下のような形で実装していました。


//ドロップダウンメニューの選択値によって値を変化させるためにuseStateを使う
//今回はドロップダウンメニューが複数あり、一つのuseState内でおさめたかったのでこんな感じで書いた
  const [selects, setSelects] = useState({
    name_id: 0,
    key_id: 0,
  })

//ドロップダウンメニュークリック時のイベントオプションを定義
  const handleDropdownChange = (event: any) => {
    const name = event.target.name
    setSelects(() => {
      return { ...selects, [name]: event.target.value }
    })
  }

//今回は配列からドロップダウンメニューを作成したかったので以下のように書く
//key_idも同じ用に書いてるので今回は省略
  List = names.map((a, index) => (
    <option value={`${a.name_id}`} key={index}>
      {a.name_id}
    </option>
  ))

//表示したい部分で下記のようにして表示
  <select name="name_id" onChange={handleDropdownChange}>
    {List}
  </select>

解決策

公式に書いてました。

参照できない理由

The SyntheticEvent is pooled. This means that the SyntheticEvent object will be reused and all properties will be nullified after the event callback has been invoked. This is for performance reasons. As such, you cannot access the event in an asynchronous way.

つまり、パフォーマンスの向上のために一度読み込まれたイベントオブジェクトは再利用されて、すべてのプロパティがnull化されてしまうので、非同期的にイベントにアクセスすることはできないということですね。

私が上記で一回目は問題なくドロップダウンメニューで選択できていたのは、イベントコールバックが一回目の呼び出しだったからで、二回目以降は最初に呼び出されたイベントオブジェクトが再利用されてしまうので、そのときに違う値が選択されているとエラーが発生する(=非同期的にアクセスできない)という理由だったみたいです。

というわけで解決策。解決策も公式に書いてました。

Note:
If you want to access the event properties in an asynchronous way, you should call event.persist() on the event, which will remove the synthetic event from the pool and allow references to the event to be retained by user code.

つまり、非同期でアクセスさせるためにはevent.persist()を使ってくれということです。

解決

//ドロップダウンメニュークリック時のイベントオプションを定義
  const handleDropdownChange = (event: any) => {
    event.persist(); //これを追加
    const name = event.target.name
    setSelects(() => {
      return { ...selects, [name]: event.target.value }
    })
  }

上記で解決!