## 仕様
><img src="../../image/2025-09-14-01-34-12.png" width="300">
- 体重、身長を入力する
- Submitを押すと、BMIを計算して表示する。
- BMIの表示と同時に、値に応じたメッセージ(低すぎる、適正です、高すぎる)
- reloadボタンを押すと初期状態にリセットできる
## 実装
> <video controls src="https://i.gyazo.com/a6ed33278e8e871ebfe4ad28bda38733.mp4" width="300" title="BMIトラッカー"></video>
```bash
BMICalculator
├── カードコンテナ
├── タイトル
├── 入力フォーム
│ ├── 体重入力
│ ├── 身長入力
│ ├── Submitボタン
│ └── Reloadボタン
└── 結果表示エリア
├── BMI値
└── 分類メッセージ
```
### TODO
- clineで実装したら理想形を一発で作ってくれた
- 今回のようなサーバにデータを送信しないSPAで完結する処理の入力フォームの場合、onSubmitを使う?onClickを使う?
- onSubmitとonClickの挙動の違い → [[e.preventDefault()]]
## CSSのマークアップ
- mainはフレキシブルボックス、justify,align-itemで中央寄せ
- 100vh ちゃんと適切に使えるべきだな・・・
- cardもフレキシブルボックス、flex-directionはcolumnにして、alignは中央寄せ
- タイトル部分は下にmarginをとってる
- form要素はフレキシブルボックス、flex: column, gap: 20pxにしている
- さらに、inpugGroupのclassで要素をまとめて、gap: 8pxにしている #CSSテク
- input要素はbox-sizing: border-boxを使って管理している
- focus時に枠線と背景が変わるようにしており、親のinputにtransitionで枠線がゆっくり変わるように指定している #CSSテク
- submitボタンは3つの状態をもつ
- 非活性: `:disabled`
- 活性
- 活性(ホバー): `hover:not(:disabled)`
## フレキシブルボックスの子要素も都度フレキシブルボックスのディスプレイタイプにする必要がある
子要素の内部にさらに柔軟なレイアウトを適用したい場合にのみ、子要素をフレックスボックスにする必要がある。
まとめ:
- 親の `display: flex` は、その直下の子要素のレイアウトを制御する。
- 子要素の `display: flex` は、その子要素自身の内部にある要素のレイアウトを制御する。
## 100vhは仮想キーボードなど、一部状況ではみ出る場合がある
[[vh]]はビューポートの100%の高さを示す。
`height: 100%`と違い、割り当て可能な要素サイズではなく表示領域の高さに依存した指定となっている。
便利だと思われるが、一部状況ではみ出る場合もありユーザビリティを下げる場合がある。
はみ出ないように調整した仮想領域を指定する`svh`が全ブラウザ対応した(2022年)らしいので、こちらを使った方が良いかも。
[CSSのsvh・dvhが全ブラウザ対応 - zenn](https://zenn.dev/tonkotsuboy_com/articles/svh-dvh-lvh-for-all-browser)
## formのonSubmitでリロードしてほしくなければ、`e.preventDefault()`
### フォームのデフォルトの挙動
フォームは、サーバーにデータを送信するために設計されたHTML要素。
フォームのonSubmitイベントは、「フォームの送信」という一連のアクションをトリガーとなる。
そのため、フォームが送信されると、ブラウザは自動的にページのリロードを行い、action属性に指定されたURLへデータを送信する。
この時、ブラウザのデフォルトのリロード処理の挙動を止めたい場合は、onSubmitのイベントハンドラとして、
`e.preventDefault()`を使いリロードのキャンセル処理を含めた関数を渡す必要がある。
### onSubmitとonClickの使い分け
複数の入力項目をまとめて処理し、サーバーへ送信する目的があるか?
- はい:formのonSubmit
- いいえ:buttonのonClick
ユーザーは「タスクの完了」を目指しているか、それとも「UIの操作」を目指しているか?
- タスクの完了:formのonSubmit
- UIの操作:buttonのonClick
## イベントハンドラで望ましい型定義を付与するためには
`onSubmit={handleSubmit}`のように、[[イベントハンドラ]]として渡すコンポーネント内関数の型定義について。
たくさん提供されているイベントハンドラの型の中からどれを選択すればよいのか?
→ボトムアップ的なアプローチだと、関数内で操作したいeの関数、プロパティを持つ型を引数にとればよい([react.dev](https://react.dev/reference/react-dom/components/common#react-event-object-methods))
### イベントハンドラの型付けの基本構造
[[React.SyntheticEvent]]がブラウザのネイティブなDOMイベントをラップする抽象化レイヤーとなっている。
reactの`index.d.ts`の中から、`extends SyntheticEvent`でフィルタすると12個の派生型があることがわかる。
せいぜい12個しかないので、この中から適切なものを選べばよい。
今回は、React.SyntheticEventの派生型(`React.FormEvent`)に依存することが、最も安全かつ推奨されるアプローチとなる。
| 型名 | 用途・意味 | 具体例のイベント | 使い分けポイント |
|---------------------|---------------------------------|--------------------------------|------------------------------------------------------------|
| React.SyntheticEvent | 全ての合成イベントの基底型 | 汎用イベント | 特に要素タイプに依存しない汎用的なイベント処理に使う。 |
| React.MouseEvent | マウス関連イベント | onClick, onDoubleClick, onMouseMove | クリックやマウスの動きに対応。マウス固有のプロパティ(ボタン番号など)を型安全に扱いたい時。 |
| React.KeyboardEvent | キーボード入力関連イベント | onKeyDown, onKeyPress, onKeyUp | キー入力の検知に使用し、押されたキーコードなどが得られる。 |
| React.FocusEvent | フォーカス取得・喪失イベント | onFocus, onBlur | フォーカス移動に関するイベントで、入力やUIコントロールのフォーカス管理に使う。 |
| React.FormEvent | フォーム送信などのフォーム関連イベント | onSubmit, onReset | フォーム送信時の処理などに利用。特にフォーム全体に関わるイベント。 |
| React.ChangeEvent | 入力欄・選択要素の値変更イベント | onChange | テキスト入力やセレクトボックスの値の変化を監視。input要素の型パラメータでより厳密に扱える。 |
| React.DragEvent | ドラッグ&ドロップ操作イベント | onDrag, onDrop | ドラッグ操作を扱う際のイベント。ドラッグオーバーやドロップ検知用。 |
| React.ClipboardEvent | クリップボードへのアクセスイベント | onCopy, onCut, onPaste | コピー・切り取り・貼り付けイベント。クリップボード操作検知に使う。 |
| React.WheelEvent | マウスホイールのスクロール関連イベント | onWheel | ホイールの動きを検知するのに利用。スクロール距離などのプロパティを持つ。 |
| React.TouchEvent | タッチスクリーンに関するイベント | onTouchStart, onTouchEnd | モバイルデバイスのタッチ入力イベントで利用する。 |
| React.PointerEvent | ポインター(マウス・タッチ・ペンなどの入力統合)イベント | onPointerDown, onPointerUp | マウスやタッチなど複数の入力を統合して扱うポインターイベント用。 |
| React.AnimationEvent | CSSアニメーションの開始・終了イベント | onAnimationStart, onAnimationEnd | CSSアニメーションに関するイベント。アニメーションのライフサイクル追跡に使う。 |
参考
- [エディタの型推論をもとに取得する - Zenn](https://zenn.dev/dollaga_saiore/scraps/e9f48d3084a0a0)
- [Gemini DeepResearch](https://gemini.google.com/app/541ef67ab6162b22?utm_source=app_launcher&utm_medium=owned&utm_campaign=base_all)