Part 3 · 画面に動きをつける
イベントハンドラ
onClick に渡すのは「関数」であって「実行結果」ではない
押されたら何かする。入力されたら何かする。 アプリを作るというのは、突き詰めればこの繰り返しです。
React では、それを onClick や onChange といった props で書きます。渡すのは関数で、呼ぶタイミングは React が決めます。
Part 0 でやった「関数を値として渡す」が、そのまま実物になります。
渡すのは関数、括弧は付けない
onClick には「押されたときに呼んでほしい関数」を渡します。
// ○ 関数そのものを渡す
<Button onClick={reset}>リセット</Button>
// ✕ その場で実行され、結果が渡ってしまう
<Button onClick={reset()}>リセット</Button>下の書き方をすると、画面が表示された瞬間に実行されます。 ボタンを押す前に動いてしまうわけです。 Part 0-2 でやった「括弧は今すぐ実行しろの合図」がそのまま当てはまります。
引数を渡したいときは包む
「押されたら setSelected("さとう") を呼びたい」 という場合、括弧を付けたら実行されてしまいます。 そこで、それを呼ぶだけの関数を新しく作って渡します。
onClick={() => setSelected("さとう")}引数のある処理は関数で包んで渡している
まだ選んでいません
map の中でボタンを作り、それぞれに違う値を渡しています。 この形は実際のアプリで山ほど出てきます。
click-basic.tsx
"use client";
import { Button } from "@/components/ui/button";
import { useState } from "react";
const members = ["さとう", "すずき", "たかはし"];
export function ClickBasic() {
const [selected, setSelected] = useState("まだ選んでいません");
// 引数のいらない関数は、そのまま渡す
const reset = () => setSelected("まだ選んでいません");
return (
<div className="flex flex-col gap-4">
<p className="rounded-md border p-3">{selected}</p>
<div className="flex flex-wrap gap-2">
{members.map((name) => (
<Button
key={name}
size="sm"
variant="outline"
// 引数を渡したいときは、それを呼ぶ関数で包む
onClick={() => setSelected(`${name} を選びました`)}
>
{name}
</Button>
))}
<Button size="sm" onClick={reset}>
リセット
</Button>
</div>
</div>
);
}何が起きたかを受け取る
ハンドラは、呼ばれるときに何が起きたのかを説明するオブジェクト を受け取ります。慣習的に event や e と名付けます。
いちばんよく使うのは、入力欄の値を取り出す場面です。
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setText(event.target.value);
};event.target… 出来事が起きた要素そのもの(この場合は入力欄)event.target.value… いま入力されている文字
打つたびにハンドラが呼ばれている
まだ入力されていません
event-object.tsx
"use client";
import { Input } from "@/components/ui/input";
import { useState } from "react";
export function EventObject() {
const [text, setText] = useState("");
// ハンドラは、何が起きたかを説明するオブジェクトを受け取る
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
// event.target が入力欄そのもの。その value が今の文字
setText(event.target.value);
};
return (
<div className="flex flex-col gap-3">
<Input placeholder="何か入力してみる" onChange={handleChange} />
<p className="rounded-md border p-3">
{text === "" ? "まだ入力されていません" : `${text.length} 文字: ${text}`}
</p>
</div>
);
}よく使うイベント
<button onClick={...}> // 押された
<input onChange={...}> // 入力が変わった
<form onSubmit={...}> // 送信された
<input onFocus={...}> // 入力欄が選ばれた
<input onBlur={...}> // 入力欄から離れた
<div onMouseEnter={...}> // マウスが乗ったいずれも on + 出来事 という名前で、 キャメルケースで書きます。JSX の章でやったとおり、 JavaScript の命名にそろえてあるためです。
既定の動きを止める
フォームの送信ボタンを押すと、ブラウザは既定でページを再読み込みします。 React で作ったアプリではたいてい困るので、これを止めます。
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault(); // 再読み込みを止める
// ここで送信の処理を書く
};click-basic.tsx
"use client";
import { Button } from "@/components/ui/button";
import { useState } from "react";
const members = ["さとう", "すずき", "たかはし"];
export function ClickBasic() {
const [selected, setSelected] = useState("まだ選んでいません");
// 引数のいらない関数は、そのまま渡す
const reset = () => setSelected("まだ選んでいません");
return (
<div className="flex flex-col gap-4">
<p className="rounded-md border p-3">{selected}</p>
<div className="flex flex-wrap gap-2">
{members.map((name) => (
<Button
key={name}
size="sm"
variant="outline"
// 引数を渡したいときは、それを呼ぶ関数で包む
onClick={() => setSelected(`${name} を選びました`)}
>
{name}
</Button>
))}
<Button size="sm" onClick={reset}>
リセット
</Button>
</div>
</div>
);
}理解できたか確かめる
確認クイズ
onClick={handleClick()} と書くとどうなる?
確認クイズ
入力欄の値を取り出すには?
click-basic.tsx
"use client";
import { Button } from "@/components/ui/button";
import { useState } from "react";
const members = ["さとう", "すずき", "たかはし"];
export function ClickBasic() {
const [selected, setSelected] = useState("まだ選んでいません");
// 引数のいらない関数は、そのまま渡す
const reset = () => setSelected("まだ選んでいません");
return (
<div className="flex flex-col gap-4">
<p className="rounded-md border p-3">{selected}</p>
<div className="flex flex-wrap gap-2">
{members.map((name) => (
<Button
key={name}
size="sm"
variant="outline"
// 引数を渡したいときは、それを呼ぶ関数で包む
onClick={() => setSelected(`${name} を選びました`)}
>
{name}
</Button>
))}
<Button size="sm" onClick={reset}>
リセット
</Button>
</div>
</div>
);
}この章のまとめ
onClickには関数を渡す。括弧を付けない- 引数が必要なときは
() => f(引数)で包む - ハンドラは event を受け取る。 入力値は
event.target.value - 名前は on + 出来事 のキャメルケース
- フォームの再読み込みは
event.preventDefault()で止める
click-basic.tsx
"use client";
import { Button } from "@/components/ui/button";
import { useState } from "react";
const members = ["さとう", "すずき", "たかはし"];
export function ClickBasic() {
const [selected, setSelected] = useState("まだ選んでいません");
// 引数のいらない関数は、そのまま渡す
const reset = () => setSelected("まだ選んでいません");
return (
<div className="flex flex-col gap-4">
<p className="rounded-md border p-3">{selected}</p>
<div className="flex flex-wrap gap-2">
{members.map((name) => (
<Button
key={name}
size="sm"
variant="outline"
// 引数を渡したいときは、それを呼ぶ関数で包む
onClick={() => setSelected(`${name} を選びました`)}
>
{name}
</Button>
))}
<Button size="sm" onClick={reset}>
リセット
</Button>
</div>
</div>
);
}