React基礎②

useState、イベント処理、フォーム

前回の復習

React基礎のおさらい

JSX = JavaScript + HTML風の文法

// 変数の表示
const name = "Ichigo";
<h1>Hello, {name}!</h1>

// 条件表示
{isLoggedIn ? <Profile /> : <Login />}
{user.isAdmin && <AdminPanel />}

// リスト表示  
{todos.map(todo => 
  <li key={todo.id}>{todo.text}</li>
)}
{}でJavaScript、keyは必須

コンポーネント = 関数

// コンポーネント定義(大文字で開始)
function UserCard({ name, email, children }) {
  return (
    <div className="card">
      <h3>{name}</h3>
      <p>{email}</p>
      {children}
    </div>
  );
}

// 使用
<UserCard name="Kurotsuchi Mayuri" email="m.kurotsuchi@seireitei.co.jp">
  <button>編集</button>
</UserCard>
props = 関数の引数、children = タグの中身

コンポーネント合成

// 小さいコンポーネント
function Header() { return <h1>ジョジョファンサイト</h1>; }

function Navigation() {
  return (
    <nav>
      <a href="#home">ホーム</a>
      <a href="#stands">スタンド</a>
    </nav>
  );
}

function Footer() { return <p>© 2025 ジョジョファンサイト</p>; }
// 合成して大きいコンポーネント  
function App() {
  return (
    <div>
      <Header />
      <Navigation />
      <main>メインコンテンツ</main>
      <Footer />
    </div>
  );
}

イベント処理

ユーザーの操作に反応する

クリック、入力などの操作を処理する方法

イベントとは?

  • ユーザーの操作(クリック、入力など)
  • Reactではイベントハンドラーで処理
HTML: onclick="doSomething()" → React: onClick={handleClick}

基本:onClickイベント


function GreetButton() {
  const handleClick = () => {
    alert('こんにちは!');
  };

  return (
    <button onClick={handleClick}>
      挨拶する
    </button>
  );
}
        
関数を{ }内で参照、実行しない(handleClick vs handleClick())

インライン関数


function QuickButton() {
  return (
    <button onClick={() => alert('クイック挨拶')}>
      クリック
    </button>
  );
}
        
短い処理ならインラインでもOK

イベントオブジェクト


function InfoButton() {
  const handleClick = (event) => {
    console.log('クリック位置:', event.clientX, event.clientY);
    console.log('ボタンテキスト:', event.target.textContent);
    console.log('イベント種類:', event.type);
  };

  return <button onClick={handleClick}>情報</button>;
}
        
eventオブジェクトの内容はイベント種類により異なる
マウス: clientX/Yキーボード: key、入力: target.value など
詳細: MDN Event API

複数ボタンの処理

アロー関数でデータを包んで渡す

入力イベント:onChange

event.target.valueで入力値を取得

その他のよく使うイベント

イベント処理 まとめ

基本パターン

  • onClick クリック処理
  • onChange 入力値変更
  • onFocus/onBlur フォーカス

重要ポイント

  • 関数参照を渡す(実行しない)
  • データ渡しにはアロー関数
  • event.target.valueで値取得

useState フック

状態管理の基礎

Reactで動的なUIを作る第一歩

なぜ状態が必要?

静的(前回まで)


function WelcomeMessage() {
  return <h1>こんにちは!</h1>;
}
                        

→ 常に同じ表示

名刺、看板、固定メニュー

動的(今回)


function LikeButton() {
  const [likes, setLikes] = useState(0);
  return <button>♡ {likes}</button>;
}
                        

→ ユーザー操作で変化

SNS、ショッピングカート、フォーム

useStateとは?

  • Reactのフックの一つ
  • コンポーネントに状態を持たせる
  • 状態が変わると→コンポーネントが再レンダリング


基本構文


const [状態変数, 更新関数] = useState(初期値);
                    

基本例:いいねボタン


動作の流れ

  1. useState(0) → 初期値0でいいね数を作成
  2. ボタンクリック → setLikes(likes + 1)
  3. 状態更新 → コンポーネント再レンダリング
  4. 新しい値でUI更新

重要:状態が変わるたびに関数コンポーネント全体が再実行される

真偽値の切り替え:ダークモードのトグル

ポイント: prev => を使うと前の状態に確実にアクセスできる

理解度チェック①【useState基礎】

useState(10)の戻り値は?


const result = useState(10);
console.log(result);
        
A) 10
B) {value: 10, setValue: function}
C) [10, function]
D) function
答え: C) useStateは配列を返す [現在の値, セッター関数]

オブジェクト状態の管理

配列状態の管理

理解度チェック②【オブジェクト更新】

オブジェクト状態の更新、どれが正しい?


const [user, setUser] = useState({name: '一護', age: 17});
// ageを18に変更したい
        
A) user.age = 18;
B) setUser({age: 18});
C) setUser({...user, age: 18});
D) setUser(user.age = 18);
答え: C) スプレッド構文で既存データを保持しつつ特定の値だけ更新

理解度チェック③【再レンダリング】

コンポーネントが再レンダリングされるのは?


const [items, setItems] = useState(['りんご', 'みかん']);
// 以下のうち、画面が更新されるのは?
        
A) items.push('ぶどう');
B) setItems([...items, 'ぶどう']);
C) items[0] = 'いちご';
D) console.log(items);
答え: B) セッター関数を使った時だけ再レンダリング
A、Cは配列を直接変更するため、Reactが変化を検知できない

よくある間違い

❌ ダメな例


// 直接変更はNG
count = count + 1;

// オブジェクト直接変更もNG
shinigami.squad = 6;

// 配列直接変更もNG
members.push('新メンバー');
                        

✅ 正しい例


// セッター関数を使う
setCount(count + 1);

// 新しいオブジェクトを作る
setShinigami({...shinigami, squad: 6});

// 新しい配列を作る
setMembers([...members, '新メンバー']);
                        

理由:Reactは参照が変わらないと再レンダリングしない。 pushやshift、popは元の配列を変更するため、Reactが変化を検知できない。

実践のコツ

前の状態を確実に使う


// こうすると2回実行されても安心
const doubleIncrement = () => {
  setCount(prev => prev + 1);
  setCount(prev => prev + 1);  // +2される
};
            

真偽値のトグル


// 長い
setIsDark(isDark ? false : true);

// シンプル
setIsDark(prev => !prev);
            
prev => を使うと前の状態に確実にアクセスできる

複数のuseState vs 単一オブジェクト

複数のuseState


const [name, setName] = useState('一護');

const [age, setAge] = useState(16);

const [isOnline, setIsOnline] = useState(false);
      

○ 独立して更新できる

○ シンプルな更新

✗ 状態が多いと管理が大変

単一オブジェクト


const [user, setUser] = useState({
  name: '一護',
  age: 16,
  isOnline: false
});
      

○ 関連する状態をまとめられる

✗ 更新時にスプレッド構文が必要

✗ 一部だけ変更でも全体が再作成

目安: 関連する2-3個の値 → オブジェクト、独立した値 → 別々のuseState

useState まとめ

できるようになったこと

  • コンポーネントに状態を持たせる
  • ユーザーの操作で画面を更新
  • 複数の状態を管理
  • オブジェクト・配列の状態更新

重要ポイント

  • 必ずセッター関数を使う
  • オブジェクト・配列は新しく作る
  • 関数型更新を活用
  • 状態更新 → 再レンダリング

フォームと入力

ユーザーからのデータ入力を受け取る

Reactでフォームを扱う基本を学ぶ

Reactでフォームが特別な理由

普通のHTML
DOM が状態を管理
React
コンポーネントが状態を管理したい
問題
どちらが正しい状態?
// uncontrolled - Reactが状態を知らない
<input type="text" />

// controlled - Reactがすべて管理
<input value={state} onChange={setState} />

フォームの基本

  • ユーザーからの入力を受け取るためのUI部品
  • テキストボックス、チェックボックス、ラジオボタンなど
  • Reactでは状態管理と組み合わせて使う
  • Web標準のform要素をそのまま活用

基本的な「Controlled Component」

ポイント: value + onChange でReactが完全に状態管理

「Uncontrolled Component」

useRefでDOM要素への参照を作成、ref.currentでアクセス

useStateと違って値が変わっても再レンダリングしない
使い分け: 簡単なフォーム→「Uncontrolled」、リアルタイム処理→「Controlled」

複数の入力フィールドを効率的に

効率化: 1つのhandlerで複数input管理、name属性が重要

様々な入力タイプを一つのstateで管理するパターン

重要な罠:inputの値は全部文字列!

問題:なぜこのコードは動かない?

// ❌ バグのあるコード 
const [age, setAge] = useState(20);

const handleChange = (e) => {
 setAge(e.target.value); // value は常に文字列!
};

// 結果
console.log(age + 1);        // "201"(文字列結合)
console.log(age > 18);       // 文字列比較でバグる
console.log(typeof age);     // "string"
HTML仕様: すべての input の value は文字列として返される
<input type="number"> でも例外なし!

解決策:型変換を明示的に行う

個別変換パターン

const handleAgeChange = (e) => {
 setAge(Number(e.target.value));
};

const handleNameChange = (e) => {
 setName(e.target.value); // 文字列のまま
};

統一ハンドラーパターン

const handleChange = (e) => {
 const { name, value, type, checked } = e.target;

 setData(prev => ({
   ...prev,
   [name]: type === 'checkbox' ? checked :
           type === 'number' ? Number(value) :
           value
 }));
};
覚え方: 「input の value は常に string、数値が欲しいなら Number() で変換」

フォーム送信とpreventDefault

重要: preventDefault()がないとページが勝手にリロードされる

FormData APIでかんたんデータ取得

React stateを使わずに、送信時だけデータを取る方法

いつ使う? 送信時だけデータが必要な場合
メリット: stateいらず、シンプル

どのパターンを使うべき?

状況 推奨パターン 理由
リアルタイム表示
(文字数カウンター、プレビューなど)
Controlled stateが即座に反映
送信のみ
(入力中は何もしない)
FormData API シンプル、高パフォーマンス
バリデーション
(入力チェック)
Controlled リアルタイムエラー表示
ファイルアップロード Uncontrolled ファイルはstateに入らない
既存のライブラリ使用 Uncontrolled ライブラリがDOM操作
基本方針: 迷ったらControlled、シンプルならFormData API

フォーム検証の種類

  • 組み込み検証 HTML属性で必須・形式・範囲などを定義
  • JavaScript検証 HTML検証の強化・カスタマイズ用
HTML検証 → 高速・低カスタマイズ
JS検証 → 柔軟・追加機能向け
基本はHTML+必要に応じてJS

組み込み検証の主な属性

  • required 必須入力
  • minlength/maxlength 文字数制限
  • min/max/step 数値の範囲・刻み
  • type 入力型(email, number等)
  • pattern 正規表現マッチ

基本的なバリデーション

まずはHTML標準機能を使った基本的な検証から

ポイント: JavaScript不要、ブラウザが自動処理、CSSで見た目制御
メリット: 軽量・高速・アクセシブル
デメリット: カスタマイズ制限、ブラウザ依存

【念のため】フォームのアクセシビリティ

  • label要素: すべての入力フィールドに適切なlabel
  • htmlFor/id: labelとinputを関連付け
  • fieldset/legend: 関連する入力項目をグループ化
  • aria-invalid: バリデーションエラー時の状態表示
  • role="alert": エラーメッセージをスクリーンリーダーに通知

<fieldset>
  <legend>個人情報</legend>
  <label htmlFor="user-name">名前(必須)</label>
  <input 
    id="user-name"
    type="text"
    required
    aria-describedby="name-error"
    aria-invalid={hasError ? 'true' : 'false'}
  />
  <div id="name-error" role="alert">
    {error && error}
  </div>
</fieldset>

フォームのベストプラクティス

  • web標準重視: HTML5のtype属性を活用(email、tel、url等)
  • 適切なsemantic markup: form、fieldset、legend要素
  • Controlled vs Uncontrolled: 用途に応じて選択
  • パフォーマンス: 不要な再レンダリングを避ける
  • UX重視: リアルタイムフィードバック、明確なエラー表示
  • セキュリティ: サーバーサイドでも必ずバリデーション
基本思想: ReactはHTMLフォームを拡張するもの、完全に置き換えるものではない

フォームでやりがちなミス

❌ 動かないコード

// ミス1: 複数input管理でname属性がない(単一inputなら不要)
<input value={email} onChange={handleChange} />
<input value={name} onChange={handleChange} />

// ミス2: onChangeがある、value属性がない  
<input name="text" onChange={handleChange} />

// ミス3: input type="number"でvalueを使う
const handleAgeChange = (e) => {
 setAge(e.target.value); // 文字列のまま
};

// ミス4: checkboxでvalue使用
<input type="checkbox" checked={agree}
  onChange={(e) => setState(e.target.value)} />

// ミ5: preventDefault忘れ
const handleSubmit = (e) => {
  console.log('送信'); // ページリロード
};

✅ 正しいコード

// name属性で識別
<input name="name" value={name} onChange={handleChange} />
<input name="email" value={email} onChange={handleChange} />

// valueでcontrolled component
<input name="text" value={text} onChange={handleChange} />

// type="number"はNumber()で変換
const handleAgeChange = (e) => {
 setAge(Number(e.target.value)); // 数値に変換
};

// checkboxはchecked
<input type="checkbox" checked={agree}
  onChange={(e) => setState(e.target.checked)} />

// リロード防止
const handleSubmit = (e) => {
  e.preventDefault();
};

理解度チェック④【フォーム基本】

このコードの問題点は?

function BrokenForm() {
  const [name, setName] = useState('');

  return (
    <input 
      type="text" 
      onChange={(e) => setName(e.target.value)}
    />
  );
}
A) onChangeが間違っている
B) value属性がない
C) setNameの書き方が間違い
D) 問題ない
答え: B) Controlled Componentにはvalue属性が必須

理解度チェック⑤【複数入力管理】

name="character"のinputに「圭一」を入力した時、stateはどうなる?

const [data, setData] = useState({ character: '', role: 'student' });

const handleChange = (e) => {
  setData(prev => ({ 
    ...prev, 
    [e.target.name]: e.target.value 
  }));
};
A) { character: '圭一' }
B) { character: '圭一', role: 'student' }
C) { 圭一: true, role: 'student' }
D) エラーになる
答え: B) スプレッド構文で既存プロパティを保持しつつ更新

まとめ

  • Controlled Component: Reactが状態管理、リアルタイム処理向け
  • Uncontrolled Component: DOM直接操作、シンプル送信向け
  • preventDefault(): フォーム送信でのページリロード回避
  • 統一的なhandleChange: name属性 + 計算プロパティで効率化

React 実践演習

1つの課題 (15-20分)

今日学んだ内容を実際に使ってみよう!

課題2 斬魄刀ステータス エディター

基本的なフォームを作成してください

  • 2つの入力フィールド: name(テキスト) と powerLevel(数値, 1-10000)
  • 両方とも必須項目
  • 送信時: データを console.log して、フォームをクリア
  • useState でフォーム状態を管理
function ZanpakutoForm() {
  const [zanpakuto, setZanpakuto] = useState({
    name: "",
    powerLevel: 1,
  });

  return <form>{/* 入力フィールドと送信ボタンを追加 */}</form>;
}

課題2 斬魄刀ステータス エディター

第2回まとめ

今日学んだこと

  • イベント処理
    onClick={handleClick} でユーザー操作をキャッチ、関数参照で渡す
  • useState
    [state, setState] = useState(初期値) で状態管理、更新で自動再レンダリング
  • フォームと入力
    React管理「Controlled」vs DOM直接「Uncontrolled」、フォームの基本的な使い方

宿題・実践課題

完全な斬魄刀フォームを作成してください

次回予告

React 応用①: useEffect、SPA