概要
ReactもJavaScriptも初心者ですので、間違いなどあったらご指摘お願いいたします。
「TODOアプリ」を作成時、未完了のTODOリストに要素の「追加」ボタンと「完了」ボタン、「削除」ボタンを実装していた。
各ボタンに対してonClickを実装した際に、ボタンによって適切な記述の仕方が違ったので、まとめておきます。
onClickとは
onClickは、要素がクリックされたときに実行されるイベントハンドラーを定義するための特別な属性(イベントハンドラープロパティ)です。
<button onClick={onClickHandler}>ボタン1</button>
上記のコードでは、「ボタン1」を押下(click)すると、onClickHandler関数が渡されます。
要素の「追加」ボタン
要素の「追加」ボタンは以下のように、onClickAdd関数を渡すことで実現できます。
<button onClick={onClickAdd}>追加</button>
この時以下の点を理解できていなかったため、「完了」ボタンと「削除ボタン」の実装時に、予期せぬ挙動を生み出してしまいました。
- onClickAdd ← これは「関数の名前(参照)」です
-
onClickAdd関数では、useStateを使ってset関数を利用している - onClick={onClickAdd} ← React に「この関数をクリック時に実行してね」と渡しているだけ
要素の「完了」ボタンと「削除」ボタン
これらも要素の「追加」ボタンと同様に記述すれば良いと考えたのが、間違いでした。
<間違った例> indexは完了する要素のキー
<button onClick={onClickComplete(index)}>完了</button>
-
関数()という表記になっているため、onClickComplete関数が即座に実行される -
onClickComplete(index)関数では、useStateを使ってset関数を利用している - Reactがコンポーネントを再レンダリングする度に実行されるため、戻り値(多くの場合 undefined)を React に渡すことになる
→ その結果、ボタンを表示した瞬間に実行されてしまう。
しかも、それが再レンダリングのたびに起きると「複数回実行されたように見える」のです。
(試しにonClickComplete関数の中身をalert()に変更すると、ページが表示された瞬間に何回もウインドウが表示されます。)
解決法
「完了」ボタンと「削除」ボタンは、引数indexを取る必要があるため、関数(index)の形式を取らざるを得ない。
そのため、解決法としては、アロー関数を使って関数の宣言をすることで、クリックされた時(後で実行する)関数を渡すことができる。
<正しい例>
<button onClick={() => onClickComplete(index)}>完了</button>
「削除」ボタンもこれと同様に対処します。
まとめ
| 書き方 | 実行タイミング | よくある要素 |
|---|---|---|
| onClick={onClickAdd} | クリックされたとき | 引数なしの関数 |
| onClick={onClickComplete(index)} | 即座に実行される(ダメ!) | バグの元 |
| onClick={() => onClickComplete(index)} | クリックされたとき | 引数ありの関数を渡す時 |