ここからはステート編として、Reactの根幹を支える概念であるステートを学んでいきます。
ステートに入る前に、まず画面からの入力を受け取るイベントを知っておく必要があります。ボタンを押した、入力欄の値が変わった、といったきっかけがあってはじめてステートが動くためです。
というわけで、ステート編の1本目はイベントから学習していきます。
事前準備
このセクションからは、新しくプロジェクトを作成して進めていきます。
npx create-next-app@latest state-app
オプションは基本的にデフォルトのままでOKです(TypeScript・src/ディレクトリ・App Routerはいずれも Yes)。
作成できたらプロジェクトに移動して、アプリケーションを起動します。
cd state-app
npm run dev
http://localhost:3000が表示されればOKです。
最終的なフォルダ構成は以下の通りです。このセクションではExample.tsxを書き換えながら進めていきます。
state-app/
└── src/
├── app/
│ └── page.tsx
└── components/
├── Example.tsx ← 新規作成
└── Example.css ← 新規作成
page.tsxはExampleを呼ぶだけにしておきます。中身は最初から書かれているものを、すべて以下に置き換えてください。
//page.tsx
import Example from "../components/Example";
const Home = ()=> {
return (
<Example />
);
}
export default Home;
componentsディレクトリを作成して、その中にExample.tsxを作ります。
//Example.tsx
import "./Example.css"
const Example = ()=> {
return (
<div className="component">
<h3>Hello State</h3>
</div>
);
}
export default Example;
同じ場所にExample.cssも作っておきましょう。このセクションで使うスタイルをまとめて書いておきます。
/* Example.css */
.component {
padding: 20px;
font-size: 20px;
}
.box {
border: 2px solid orange;
padding: 20px;
margin-top: 12px;
}
画面にHello Stateと表示されたら準備完了です。
イベントリスナーとは
イベントリスナーとは、画面上でイベントが発生した際に実行したい関数を登録しておく場所だと思ってください。クリックや入力欄の値の変更など、いろいろなものがイベントとしてブラウザ上で発生し、イベントリスナーに登録されたイベントハンドラによって処理されます。
クリックイベントを登録する
まずはExample.tsxにボタンを置いて、クリックイベントを登録してみましょう。
//Example.tsx
import "./Example.css"
const Example = ()=> {
const clickHandler = ()=> {
alert("ボタンがクリックされました");
}
return (
<div className="component">
<button onClick={clickHandler}>クリックしてね</button>
</div>
);
}
export default Example;
ボタンを押すとボタンがクリックされましたというダイアログが表示されます。
ここでonClickに登録した関数がイベントハンドラです。
onClickのCは大文字である必要があります。HTMLで書く場合はonclickとすべて小文字ですが、TSXの中では大文字にする必要があります。このあと紹介するonChangeやonBlurなども同様です。
別のボタンには別の処理を登録することもできます。
//Example.tsx
const Example = ()=> {
const clickHandler = ()=> {
alert("ボタンがクリックされました");
}
const logHandler = ()=> {
console.log("ボタンがクリックされました");
}
return (
<div className="component">
<button onClick={clickHandler}>クリックしてね</button>
<button onClick={logHandler}>ログを出す</button>
</div>
);
}
アロー関数を直接書くこともできる
わざわざ関数を別で定義せず、その場にアロー関数を書いても同じ挙動になります。
<button onClick={()=> alert("ボタンがクリックされました")}>クリックしてね</button>
どちらでも構いませんが、TSXの中にTypeScriptのコードを書きすぎると可読性が悪くなるので、なるべく名前付き関数として定義して、それを登録するのがよいかと思います。
関数の末尾に丸括弧を付けてはいけない
ここは初心者のうちにやりがちなポイントです。
// これはダメ
<button onClick={clickHandler()}>クリックしてね</button>
こう書くと、ボタンを押していないのに画面表示と同時にダイアログが出てしまいます。
onClickに渡すべきなのは関数そのものであって、関数を実行した結果ではありません。末尾に丸括弧を付けると、TSXを作るタイミングでその関数が実行されてしまうからです。
なぜそうなるのか
丸括弧を付けると、その場所には関数の戻り値が設定されることになります。
//Example.tsx
const hello = ()=> {
return "Hello React";
}
const Example = ()=> {
return (
<div className="component">
<button>{hello()}</button>
</div>
);
}
この場合、ボタンの中にはHello Reactという文字列が表示されます。関数が実行され、その戻り値がここに置き換わったわけです。
丸括弧を付けない場合はどうでしょうか。
console.log(hello); // → アロー関数そのものが表示される
console.log(hello()); // → "Hello React" という文字列が表示される
では、さきほどのclickHandlerはどうなるでしょうか。
const clickHandler = ()=> {
alert("ボタンがクリックされました");
}
console.log(clickHandler()); // → undefined
clickHandlerにはreturnが書かれていないので、戻り値はundefinedになります。
つまりonClick={clickHandler()}と書くと、onClickにはundefinedが登録されることになります。undefinedをクリックイベントに登録しても何も起こりません。だから関数自体を渡す必要がある、というわけです。
アロー関数で囲む場合は丸括弧が必要
では、こう書いた場合はどうでしょうか。
<button onClick={()=> clickHandler()}>クリックしてね</button>
この場合は末尾の丸括弧が必要です。
省略せずに書くとこうなります。
<button onClick={()=> { clickHandler(); }}>クリックしてね</button>
onClickで実行されるのは外側のアロー関数で、それが実行されたときに本文が実行されます。このとき丸括弧が無いと、ただ関数が書いてあるだけで実行されません。だから丸括弧が必要になります。
いろいろなイベントタイプ
クリック以外にもよく使うイベントがあります。入力欄を使って見ていきましょう。
onChange(入力値の変更)
//Example.tsx
import "./Example.css"
const Example = ()=> {
const changeHandler = (e: React.ChangeEvent<HTMLInputElement>)=> {
console.log(e.target.value);
}
return (
<div className="component">
<input type="text" onChange={changeHandler} />
</div>
);
}
export default Example;
入力欄に文字を打つたびに、コンソールにその値が出力されます。
入力された値は、イベントハンドラの引数に渡ってくるイベントオブジェクトのtarget.valueから取得します。TSXでは引数に型が必要なので、React.ChangeEvent<HTMLInputElement>と書いておきます。
コールバック関数の引数になぜ値が渡ってくるのか分からない方は、JavaScriptの基礎のコールバック関数について学んでみてください。
onBlur / onFocus(フォーカス)
//Example.tsx
const Example = ()=> {
const focusHandler = ()=> {
console.log("フォーカスを得ました");
}
const blurHandler = ()=> {
console.log("フォーカスを失いました");
}
return (
<div className="component">
<input type="text" onFocus={focusHandler} onBlur={blurHandler} />
</div>
);
}
onFocusは入力欄がフォーカスを得たとき、onBlurはフォーカスを失ったときに発火します。この2つはinput要素と一緒によく使うので、覚えておいてください。
onMouseEnter / onMouseLeave(ホバー)
//Example.tsx
const Example = ()=> {
return (
<div
className="box"
onMouseEnter={()=> console.log("入った")}
onMouseLeave={()=> console.log("出た")}
>
ホバーしてね
</div>
);
}
要素にカーソルが入ったときと出たときに、それぞれイベントハンドラが実行されます。
JavaScriptのイベントとの違い
ここで見てきたイベントタイプは、基本的にJavaScriptのイベントと同じ名前で使えます。違うのは、JavaScriptではonmouseenterのようにすべて小文字である点だけです。
もう1点、onChangeだけ注意が必要です。
ReactのonChangeは、JavaScriptでいうoninputイベントと同じ意味になります。JavaScriptのonchangeは「入力が完了してフォーカスを失ったとき」に発火しますが、ReactのonChangeは入力値が変更されたタイミングで発火します。この点だけ覚えておいてください。
確認問題
以下のExample.tsxは、ボタンを押していないのに画面を開いた瞬間にアラートが表示されてしまいます。
//Example.tsx
import "./Example.css"
const Example = ()=> {
const clickHandler = ()=> {
alert("クリックされました");
}
return (
<div className="component">
<button onClick={clickHandler()}>クリック</button>
</div>
);
}
export default Example;
問題
- なぜ画面を開いた瞬間にアラートが出るのか説明してください
- このとき
onClickには結局何が登録されているか答えてください - 正しく動くように直してください(2通り書いてください)
- 入力欄を追加し、入力された値をコンソールに出力する
onChangeのイベントハンドラを書いてください
(下に解答を記載しています)
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
解答
1.
clickHandler()と末尾に丸括弧を付けているため、TSXを作るタイミングで関数が実行されてしまうからです。
2.
clickHandlerにはreturnが無いので、戻り値のundefinedが登録されます。そのためボタンを押しても何も起こりません。
3. 直し方
関数そのものを渡す方法。
<button onClick={clickHandler}>クリック</button>
アロー関数で囲む方法。この場合は中で実行するので丸括弧が必要です。
<button onClick={()=> clickHandler()}>クリック</button>
4. onChangeのイベントハンドラ
//Example.tsx
import "./Example.css"
const Example = ()=> {
const changeHandler = (e: React.ChangeEvent<HTMLInputElement>)=> {
console.log(e.target.value);
}
return (
<div className="component">
<input type="text" onChange={changeHandler} />
</div>
);
}
export default Example;
チェックリスト
-
onClickなどのイベント名は大文字始まりで書いている - イベントハンドラは関数そのものを渡し、末尾に丸括弧を付けていない
- 丸括弧を付けると戻り値が登録されてしまうと説明できる
- アロー関数で囲む場合は、中で実行するので丸括弧が必要だと理解している
-
入力値は
e.target.valueから取得している -
イベントオブジェクトの引数に型(
React.ChangeEvent<HTMLInputElement>など)を付けている -
onChange/onBlur/onFocusの違いが分かる -
Reactの
onChangeはJavaScriptのoninputにあたると知っている
ステート編①はここまで。
次回はuseStateについて学習していきます!