はじめに
Reactで削除ボタンを実装していたときに、
「クリックした時に実行したい処理が、画面表示時に実行されてしまう」
という挙動に遭遇しました。
この記事では、
- なぜその挙動が起きたのか
-
onClickを渡すときの書き方による違い
について整理します。
起きた問題
削除ボタンに以下のようなコードを書いていました。
<button onClick={deleteTodo(todo.id)}>
削除
</button>
Todo削除する関数を実装完了!
この関数をonClickに渡せば、削除できると思って保存したら...
画面を表示した瞬間に削除処理が実行される
という挙動になりました。
原因は何だったのか?
今回の原因は、
- 関数を「実行した結果」を渡していた
- 関数そのものを渡せていなかった
という点でした。
言い換えると、
onClickには「後で実行する処理」を渡す必要があるのに、その場で処理を実行する書き方をしてしまっていました。
ではなぜ、そのような書き方になると「画面表示時に処理が実行される」のかという疑問を次の項目で説明します。
onClickに渡す書き方で何が変わるのか
onClick={deleteTodo(todo.id)}の場合
<button onClick={deleteTodo(todo.id)}>
削除
</button>
一見すると、「削除ボタンをクリックしたらdeleteTodoが実行されそう」に見えます。
しかしこの書き方では、クリックとは関係なく、画面を表示する段階で処理が実行されます。
理由は、deleteTodo(todo.id)と書いた時点で、JavaScriptはこの関数をその場で実行してしまうからです。
そしてReactでは、画面を表示するためにJSXの中身が評価されます。
その評価の過程でdeleteTodo(todo.id)が実行されてしまうため、結果としてレンダリング(画面描画)時に削除処理が走ることとなります。
まとめると、deleteTodo(todo.id)と書いたことで、処理はクリック前に実行され、onClickには実行結果が渡っていました。
onClick={() => deleteTodo(todo.id)} の場合
<button onClick={() => deleteTodo(todo.id)}>
削除
</button>
一見すると、先ほどのコードとほとんど同じように見えます。
しかしこの書き方では、画面を表示した時点では何も実行されません。
理由は、deleteTodo(todo.id)をアロー関数の中に閉じ込めているからです。
この時点で作られているのは「削除処理そのもの」ではなく、クリックされた時に実行される関数です。
そのため、Reactが画面をレンダリングする際には処理は実行されず、ユーザーがクリックした瞬間に初めてdeleteTodoが実行されます。
まとめると、この書き方ではアロー関数を新しく作成し、「クリックされたら実行する処理」として、onClickに渡しています。
おわりに
最初は「Reactのレンダリングが原因?」と思いましたが、実際には JavaScriptの関数実行のタイミング の問題でした。
同じように「なぜかクリック前に処理が走る...」と悩んだ方の参考になれば嬉しいです。
※もし認識違いや、より良い書き方があれば、
ぜひコメントで教えてください。