👨‍💻

【React 19.x】この記事1本でReactの基本をマスターする

に公開

はじめに

この記事では、React 19を使ったWebアプリケーション開発の基礎から実践的な内容まで、順を追って解説します。

この記事で学べること

  • React 19の開発環境構築
  • JSX記法の基本ルール
  • コンポーネント設計の考え方
  • Props、State、useEffectの使い方
  • 実践的なReact開発の知識

想定読者

  • HTML/CSS/JavaScriptの基礎知識がある方
  • Reactを初めて学ぶ方
  • React 19の新機能を知りたい方

React 19の特徴

React 19では、以下のような改善が行われています。

  • Reactコンパイラの導入: メモ化の自動最適化
  • Actionsのサポート: フォーム処理の簡略化
  • useフックの追加: 非同期処理の扱いが容易に
  • ref as props: ref属性の扱いが統一的に
  • Server Componentsの正式サポート: サーバーサイドレンダリングの強化

1. Reactを学ぶ準備

Reactとは

Reactは、Metaが開発したユーザーインターフェース構築のためのJavaScriptライブラリです。コンポーネントベースの設計により、再利用可能なUI部品を作成できます。

開発環境のセットアップ

React 19では、Viteを使った開発環境の構築が推奨されています。

# Viteでプロジェクト作成
npm create vite@latest my-react-app -- --template react

# プロジェクトディレクトリに移動
cd my-react-app

# 依存パッケージのインストール
npm install

# 開発サーバーの起動
npm run dev

必要な前提知識

Reactを学ぶ前に、以下の知識があるとスムーズです。

  • JavaScript ES6+: アロー関数、分割代入、スプレッド構文
  • HTML/CSS: 基本的なマークアップとスタイリング
  • npm/yarn: パッケージ管理ツールの基本操作

2. JSX記法のルール

JSXとは何か

JSX(JavaScript XML)は、JavaScriptの中にHTML風の記法を書ける構文拡張です。Reactでは、JSXを使ってUIを記述します。

// JSXの例
const element = <h1>Hello, React 19!</h1>;

基本的な記法ルール

JSXには、HTMLとは異なるいくつかのルールがあります。

1. 必ず1つのルート要素を返す

// ❌ 複数の要素を直接返せない
function BadComponent() {
  return (
    <h1>タイトル</h1>
    <p>本文</p>
  );
}

// ✅ 1つの要素で囲む
function GoodComponent() {
  return (
    <div>
      <h1>タイトル</h1>
      <p>本文</p>
    </div>
  );
}

// ✅ フラグメントを使う(余分なdivが不要な場合)
function BetterComponent() {
  return (
    <>
      <h1>タイトル</h1>
      <p>本文</p>
    </>
  );
}

2. タグは必ず閉じる

// ❌ HTMLでは省略できても、JSXでは必須
<img src="image.jpg">

// ✅ 自己閉じタグを使う
<img src="image.jpg" />

3. className、htmlForを使う

JavaScriptの予約語との衝突を避けるため、属性名が異なります。

// ❌ classは使えない
<div class="container">

// ✅ classNameを使う
<div className="container">

// ❌ forは使えない
<label for="name">

// ✅ htmlForを使う
<label htmlFor="name">

4. JavaScriptの式を埋め込む

波括弧 {} を使って、JavaScript式を埋め込めます。

function Greeting() {
  const name = "太郎";
  const age = 25;
  
  return (
    <div>
      <h1>こんにちは、{name}さん</h1>
      <p>あなたは{age}歳です</p>
      <p>来年は{age + 1}歳になります</p>
    </div>
  );
}

JavaScriptとの違い

JSXは内部的に React.createElement() に変換されます。

// JSX
<h1 className="title">Hello</h1>

// 変換後のJavaScript
React.createElement('h1', { className: 'title' }, 'Hello')

よくある間違いと注意点

キャメルケースで属性を書く

// ❌ ハイフン区切りは使えない
<button onclick="handleClick">

// ✅ キャメルケースで書く
<button onClick={handleClick}>

コメントの書き方

function Component() {
  return (
    <div>
      {/* これはJSX内のコメント */}
      <p>テキスト</p>
    </div>
  );
}

条件付きレンダリング

function Message({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <p>ようこそ</p>
      ) : (
        <p>ログインしてください</p>
      )}
    </div>
  );
}

3. コンポーネントの使い方

コンポーネントとは

コンポーネントは、UIを独立した再利用可能な部品として扱う概念です。Reactアプリケーションは、複数のコンポーネントを組み合わせて構築します。

関数コンポーネントの作成

React 19では、関数コンポーネントが標準です。

// 基本的な関数コンポーネント
function Welcome() {
  return <h1>ようこそReact 19へ</h1>;
}

// アロー関数でも書ける
const Greeting = () => {
  return <p>こんにちは</p>;
};

// 1行で返す場合はreturnを省略可能
const SimpleButton = () => <button>クリック</button>;

コンポーネントの呼び出し方

コンポーネントは、HTMLタグのように記述します。

function App() {
  return (
    <div>
      <Welcome />
      <Greeting />
      <SimpleButton />
    </div>
  );
}

重要なルール:

  • コンポーネント名は必ず大文字で始める
  • 小文字で始まるとHTMLタグとして扱われる
// ❌ 小文字で始めるとエラー
function welcome() {
  return <h1>Welcome</h1>;
}

// ✅ 大文字で始める
function Welcome() {
  return <h1>Welcome</h1>;
}

コンポーネント設計の基本

単一責任の原則

1つのコンポーネントは1つの役割だけを持つようにしましょう。

// ❌ 責任が多すぎる
function UserDashboard() {
  return (
    <div>
      <header>...</header>
      <nav>...</nav>
      <main>
        <article>...</article>
        <aside>...</aside>
      </main>
      <footer>...</footer>
    </div>
  );
}

// ✅ コンポーネントを分割
function Header() {
  return <header>...</header>;
}

function Navigation() {
  return <nav>...</nav>;
}

function Article() {
  return <article>...</article>;
}

function Sidebar() {
  return <aside>...</aside>;
}

function Footer() {
  return <footer>...</footer>;
}

function UserDashboard() {
  return (
    <div>
      <Header />
      <Navigation />
      <main>
        <Article />
        <Sidebar />
      </main>
      <Footer />
    </div>
  );
}

ファイル構成の例

src/
├── components/
│   ├── Header.jsx
│   ├── Navigation.jsx
│   ├── Article.jsx
│   └── Footer.jsx
└── App.jsx

4. イベントとスタイルの扱い方

イベントハンドラの実装

Reactでは、イベントハンドラを関数として渡します。

function Button() {
  // イベントハンドラ関数
  const handleClick = () => {
    console.log('ボタンがクリックされました');
  };

  return <button onClick={handleClick}>クリック</button>;
}

イベントオブジェクトの利用

function InputForm() {
  const handleChange = (event) => {
    console.log('入力値:', event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault(); // デフォルト動作を防ぐ
    console.log('フォームが送信されました');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" onChange={handleChange} />
      <button type="submit">送信</button>
    </form>
  );
}

よくあるイベント

function EventExamples() {
  return (
    <div>
      <button onClick={() => console.log('クリック')}>
        クリックイベント
      </button>
      
      <input
        onFocus={() => console.log('フォーカス')}
        onBlur={() => console.log('フォーカス外れ')}
      />
      
      <div
        onMouseEnter={() => console.log('マウスイン')}
        onMouseLeave={() => console.log('マウスアウト')}
      >
        ホバーエリア
      </div>
    </div>
  );
}

インラインスタイル

JSXでは、スタイルをオブジェクトとして記述します。

function StyledComponent() {
  const style = {
    color: 'blue',
    fontSize: '20px',
    backgroundColor: '#f0f0f0',
    padding: '10px'
  };

  return (
    <div style={style}>
      スタイル付きテキスト
    </div>
  );
}

// 直接書くこともできる
function InlineStyled() {
  return (
    <div style={{ color: 'red', margin: '20px' }}>
      インラインスタイル
    </div>
  );
}

注意点:

  • プロパティ名はキャメルケース(fontSizebackgroundColor
  • 値は文字列で指定(数値の場合は単位なしでpxが適用される)

CSSファイルの読み込み

/* styles.css */
.container {
  max-width: 1200px;
  margin: 0 auto;
}

.title {
  font-size: 24px;
  color: #333;
}
// Component.jsx
import './styles.css';

function Component() {
  return (
    <div className="container">
      <h1 className="title">タイトル</h1>
    </div>
  );
}

条件付きスタイリング

function ConditionalStyle({ isActive }) {
  // クラス名を条件で切り替え
  const className = isActive ? 'active' : 'inactive';

  return <div className={className}>コンテンツ</div>;
}

// 複数のクラスを組み合わせる
function MultipleClasses({ isActive, isLarge }) {
  const classes = [
    'base',
    isActive && 'active',
    isLarge && 'large'
  ].filter(Boolean).join(' ');

  return <div className={classes}>コンテンツ</div>;
}

// インラインスタイルで条件分岐
function DynamicStyle({ isHighlighted }) {
  return (
    <div style={{
      backgroundColor: isHighlighted ? 'yellow' : 'white',
      fontWeight: isHighlighted ? 'bold' : 'normal'
    }}>
      ハイライト可能なテキスト
    </div>
  );
}

5. Propsによるデータの受け渡し

Propsとは

Props(プロパティ)は、親コンポーネントから子コンポーネントへデータを渡す仕組みです。

Propsの渡し方と受け取り方

// 子コンポーネント:Propsを受け取る
function Greeting(props) {
  return <h1>こんにちは、{props.name}さん</h1>;
}

// 親コンポーネント:Propsを渡す
function App() {
  return <Greeting name="太郎" />;
}

分割代入でシンプルに書く

// より一般的な書き方
function Greeting({ name }) {
  return <h1>こんにちは、{name}さん</h1>;
}

// 複数のPropsを受け取る
function UserCard({ name, age, email }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>年齢: {age}歳</p>
      <p>Email: {email}</p>
    </div>
  );
}

function App() {
  return (
    <UserCard 
      name="太郎" 
      age={25} 
      email="taro@example.com" 
    />
  );
}

様々な型のPropsを渡す

function Example() {
  return (
    <Component
      text="文字列"
      number={123}
      boolean={true}
      array={[1, 2, 3]}
      object={{ key: 'value' }}
      func={() => console.log('関数')}
    />
  );
}

デフォルト値の設定

Propsが渡されなかった場合のデフォルト値を設定できます。

// デフォルトパラメータを使う
function Greeting({ name = "ゲスト" }) {
  return <h1>こんにちは、{name}さん</h1>;
}

function App() {
  return (
    <>
      <Greeting name="太郎" /> {/* こんにちは、太郎さん */}
      <Greeting /> {/* こんにちは、ゲストさん */}
    </>
  );
}

childrenプロパティ

childrenは特別なプロパティで、コンポーネントのタグ間に書かれた内容を受け取ります。

function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

function App() {
  return (
    <Card>
      <h2>カードタイトル</h2>
      <p>カードの内容</p>
    </Card>
  );
}

childrenの実践例

function Button({ children, onClick }) {
  return (
    <button onClick={onClick} className="custom-button">
      {children}
    </button>
  );
}

function App() {
  return (
    <>
      <Button onClick={() => console.log('保存')}>
        保存
      </Button>
      <Button onClick={() => console.log('削除')}>
        削除
      </Button>
    </>
  );
}

Propsの重要なルール:

  • Propsは読み取り専用(子コンポーネント内で変更できない)
  • 親から子への一方向データフロー
// ❌ Propsを直接変更してはいけない
function BadComponent({ count }) {
  count = count + 1; // エラー
  return <p>{count}</p>;
}

6. Stateによる状態管理

Stateとは

State(状態)は、コンポーネント内部で管理するデータです。Stateが変更されると、コンポーネントは自動的に再レンダリングされます。

useStateフックの使い方

useStateは、関数コンポーネントでStateを使うためのフックです。

import { useState } from 'react';

function Counter() {
  // [現在の値, 更新関数] = useState(初期値)
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  );
}

様々な型のState

function StateExamples() {
  // 数値
  const [count, setCount] = useState(0);
  
  // 文字列
  const [text, setText] = useState('');
  
  // 真偽値
  const [isOpen, setIsOpen] = useState(false);
  
  // 配列
  const [items, setItems] = useState([]);
  
  // オブジェクト
  const [user, setUser] = useState({ name: '', age: 0 });

  return <div>...</div>;
}

Stateの更新ルール

基本的な更新

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

前の値を使った更新

複数回更新する場合は、関数形式を使います。

function Counter() {
  const [count, setCount] = useState(0);

  const incrementThree = () => {
    // ❌ これは1しか増えない
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  };

  const incrementThreeCorrectly = () => {
    // ✅ 前の値を使って更新
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };

  return (
    <div>
      <p>{count}</p>
      <button onClick={incrementThreeCorrectly}>+3</button>
    </div>
  );
}

オブジェクトStateの更新

function UserProfile() {
  const [user, setUser] = useState({
    name: '太郎',
    age: 25,
    email: 'taro@example.com'
  });

  const updateName = (newName) => {
    // スプレッド構文で既存の値をコピー
    setUser({
      ...user,
      name: newName
    });
  };

  const updateAge = () => {
    setUser(prev => ({
      ...prev,
      age: prev.age + 1
    }));
  };

  return (
    <div>
      <p>{user.name} - {user.age}歳</p>
      <button onClick={() => updateName('次郎')}>名前変更</button>
      <button onClick={updateAge}>年齢+1</button>
    </div>
  );
}

配列Stateの更新

function TodoList() {
  const [todos, setTodos] = useState([]);

  // 追加
  const addTodo = (text) => {
    setTodos([...todos, { id: Date.now(), text }]);
  };

  // 削除
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  // 更新
  const updateTodo = (id, newText) => {
    setTodos(todos.map(todo => 
      todo.id === id ? { ...todo, text: newText } : todo
    ));
  };

  return <div>...</div>;
}

StateとPropsの違い

項目 State Props
データの所有 コンポーネント内部 親から受け取る
変更可能性 変更可能 読み取り専用
変更時の挙動 再レンダリング -
使用場面 動的なデータ 静的な設定値
function Example() {
  // State: 自分で管理
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      {/* Props: 子に渡す */}
      <ChildComponent value={count} />
    </div>
  );
}

function ChildComponent({ value }) {
  // Props: 親から受け取るだけ(変更不可)
  return <p>受け取った値: {value}</p>;
}

7. 再レンダリングとuseEffect

再レンダリングの仕組み

Reactコンポーネントは、以下の場合に再レンダリングされます。

function Parent() {
  const [count, setCount] = useState(0);
  
  console.log('Parentレンダリング');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>カウント</button>
      <Child /> {/* 親が再レンダリングされると子も再レンダリング */}
    </div>
  );
}

function Child() {
  console.log('Childレンダリング');
  return <p>子コンポーネント</p>;
}

useEffectの基本

useEffectは、副作用(サイドエフェクト)を扱うためのフックです。副作用とは、レンダリング以外の処理のことです。

  • API呼び出し
  • DOMの直接操作
  • タイマー設定
  • イベントリスナー登録
import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // useEffectの基本形
  useEffect(() => {
    console.log('副作用が実行されました');
  });

  return (
    <button onClick={() => setCount(count + 1)}>
      カウント: {count}
    </button>
  );
}

依存配列の理解

依存配列で、useEffectの実行タイミングを制御します。

function DependencyExample() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // パターン1: 依存配列なし → 毎回実行
  useEffect(() => {
    console.log('毎レンダリング時に実行');
  });

  // パターン2: 空の依存配列 → 初回のみ実行
  useEffect(() => {
    console.log('初回マウント時のみ実行');
  }, []);

  // パターン3: 依存配列あり → 指定値が変更時のみ実行
  useEffect(() => {
    console.log(`countが変更されました: ${count}`);
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>カウント</button>
      <input value={text} onChange={e => setText(e.target.value)} />
    </div>
  );
}

実行タイミングの比較:

クリーンアップ処理

useEffectから関数を返すことで、クリーンアップ処理を実行できます。

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // タイマー開始
    const timerId = setInterval(() => {
      setCount(prev => prev + 1);
    }, 1000);

    // クリーンアップ関数
    return () => {
      clearInterval(timerId); // タイマー停止
      console.log('タイマーをクリーンアップしました');
    };
  }, []); // 空配列なので初回のみタイマー設定

  return <p>経過時間: {count}秒</p>;
}

クリーンアップが必要な例

function WindowSize() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    // イベントリスナー登録
    const handleResize = () => {
      setWidth(window.innerWidth);
    };
    
    window.addEventListener('resize', handleResize);

    // クリーンアップでイベントリスナー解除
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []);

  return <p>ウィンドウ幅: {width}px</p>;
}

useEffectの実践パターン

パターン1: データフェッチ

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await fetch('https://api.example.com/users');
        const data = await response.json();
        setUsers(data);
      } catch (error) {
        console.error('データ取得エラー:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []); // 初回のみ実行

  if (loading) return <p>読み込み中...</p>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

パターン2: ローカルストレージとの同期

function Counter() {
  const [count, setCount] = useState(() => {
    // 初期値をローカルストレージから取得
    const saved = localStorage.getItem('count');
    return saved ? JSON.parse(saved) : 0;
  });

  useEffect(() => {
    // countが変更されたらローカルストレージに保存
    localStorage.setItem('count', JSON.stringify(count));
  }, [count]);

  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

パターン3: ドキュメントタイトルの更新

function PageTitle() {
  const [title, setTitle] = useState('ホーム');

  useEffect(() => {
    document.title = `${title} - My App`;
  }, [title]);

  return (
    <div>
      <button onClick={() => setTitle('ダッシュボード')}>
        ダッシュボード
      </button>
      <button onClick={() => setTitle('設定')}>
        設定
      </button>
    </div>
  );
}

パターン4: 依存値に基づく処理

function SearchResults({ query }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    // queryが空なら何もしない
    if (!query) {
      setResults([]);
      return;
    }

    // 検索処理
    const searchApi = async () => {
      const response = await fetch(`/api/search?q=${query}`);
      const data = await response.json();
      setResults(data);
    };

    searchApi();
  }, [query]); // queryが変わるたびに検索

  return (
    <ul>
      {results.map(item => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

useEffect使用時の注意点:

  1. 依存配列を正しく指定する
  2. 無限ループに注意(依存配列内でStateを更新しない)
  3. 非同期処理は関数内で実行(useEffect自体をasyncにしない)
// ❌ useEffect自体をasyncにしない
useEffect(async () => {
  const data = await fetchData();
}, []);

// ✅ 内部で非同期関数を定義して実行
useEffect(() => {
  const fetchData = async () => {
    const data = await fetch('...');
  };
  fetchData();
}, []);

8. モジュールのエクスポート方法

JavaScriptモジュールシステムには、2つのエクスポート方法があります。

default exportとnamed exportの違い

default export

// Button.jsx
function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

export default Button;
// App.jsx
import Button from './Button'; // 任意の名前でインポート可能
import MyButton from './Button'; // これでもOK

function App() {
  return <Button>クリック</Button>;
}

named export

// utils.jsx
export function formatDate(date) {
  return date.toLocaleDateString('ja-JP');
}

export function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export const API_URL = 'https://api.example.com';
// App.jsx
import { formatDate, capitalize, API_URL } from './utils';

// または一括インポート
import * as utils from './utils';

function App() {
  const date = formatDate(new Date());
  const name = capitalize('john');
  
  return <div>{date} - {name}</div>;
}

混在パターン

// Card.jsx
function Card({ children }) {
  return <div className="card">{children}</div>;
}

export function CardHeader({ children }) {
  return <div className="card-header">{children}</div>;
}

export function CardBody({ children }) {
  return <div className="card-body">{children}</div>;
}

export default Card;
// App.jsx
import Card, { CardHeader, CardBody } from './Card';

function App() {
  return (
    <Card>
      <CardHeader>タイトル</CardHeader>
      <CardBody>内容</CardBody>
    </Card>
  );
}

それぞれの使い分け

用途 default export named export
コンポーネント メインコンポーネント サブコンポーネント、ヘルパー
関数 主要な1つの関数 ユーティリティ関数群
定数 - 設定値、定数
推奨場面 1ファイル1コンポーネント 複数の関連する関数

実践的な使用例

// components/User/UserCard.jsx(default export)
function UserCard({ user }) {
  return (
    <div>
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </div>
  );
}

export default UserCard;
// utils/validators.js(named export)
export function validateEmail(email) {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

export function validatePassword(password) {
  return password.length >= 8;
}

export function validateUsername(username) {
  return username.length >= 3;
}

インポート方法

// default exportのインポート
import Button from './Button';
import MyButton from './Button'; // 名前は自由

// named exportのインポート
import { formatDate } from './utils';
import { formatDate, capitalize } from './utils';
import { formatDate as format } from './utils'; // 別名でインポート

// 両方のインポート
import Card, { CardHeader, CardBody } from './Card';

// 一括インポート
import * as utils from './utils';
utils.formatDate(new Date());

9. ライブラリ選定のポイント

よく使われるReactライブラリ

Reactエコシステムには、様々な用途のライブラリがあります。

ルーティング

// React Router
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">ホーム</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

状態管理

  • Zustand: シンプルで軽量
  • Redux Toolkit: 大規模アプリ向け
  • Jotai: アトムベースの状態管理
// Zustandの例
import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 }))
}));

function Counter() {
  const { count, increment } = useStore();
  return <button onClick={increment}>{count}</button>;
}

UIコンポーネントライブラリ

  • Material-UI (MUI): マテリアルデザイン
  • Chakra UI: アクセシビリティ重視
  • shadcn/ui: カスタマイズ性が高い

フォーム管理

  • React Hook Form: パフォーマンス重視
  • Formik: 機能豊富
// React Hook Formの例
import { useForm } from 'react-hook-form';

function LoginForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email', { required: true })} />
      {errors.email && <span>メールアドレスは必須です</span>}
      
      <input type="password" {...register('password')} />
      <button type="submit">ログイン</button>
    </form>
  );
}

React 19との互換性確認

React 19を使用する際は、ライブラリの互換性を確認しましょう。

# パッケージの情報を確認
npm info ライブラリ名

# React 19対応バージョンをインストール
npm install ライブラリ名@latest

確認ポイント:

  • 公式ドキュメントのReact 19対応記載
  • GitHubのissuesで互換性問題がないか
  • npmの最新バージョン日付

選定時の判断基準

1. メンテナンス状況

確認項目:

  • 最終更新日が1年以内
  • 定期的なリリース
  • GitHub Starsとフォーク数
  • 週間ダウンロード数

2. バンドルサイズ

# バンドルサイズを確認
npm install -g bundlephobia-cli
bundlephobia ライブラリ名
  • 小規模プロジェクト: 100KB未満が目安
  • 大規模プロジェクト: 機能とのトレードオフで判断

3. 学習コスト

  • ドキュメントの充実度
  • サンプルコードの豊富さ
  • コミュニティのサポート
  • 既存の知識を活かせるか

まとめ

学んだ内容の振り返り

この記事では、React 19を使った開発の基礎を学びました。

React 19の新機能:

  • Reactコンパイラによる自動最適化
  • Actionsによるフォーム処理の簡略化
  • useフックの追加

基本概念:

  • JSX記法のルール
  • コンポーネント設計
  • PropsとStateの違い
  • useEffectによる副作用管理

実践的な知識:

  • イベント処理とスタイリング
  • データフェッチ
  • モジュールのエクスポート
  • ライブラリ選定

次のステップ

React 19の学習を進めるために、以下のテーマに取り組んでみましょう。

初級から中級へ

  1. カスタムフックの作成

    • ロジックの再利用
    • コードの整理
  2. パフォーマンス最適化

    • React.memo
    • useMemo、useCallback
    • 遅延読み込み
  3. エラーハンドリング

    • Error Boundary
    • エラーの適切な処理

中級から上級へ

  1. TypeScript導入

    • 型安全性の向上
    • 開発効率の改善
  2. テスト

    • Jest、React Testing Library
    • コンポーネントテスト
  3. Server Components

    • サーバーサイドレンダリング
    • Next.jsとの統合
  4. React 19の高度な機能

    • use フック
    • Actions API
    • Reactコンパイラの活用

React 19は進化を続けているため、公式ドキュメントを定期的に確認し、最新の機能やベストプラクティスをキャッチアップしていきましょう。

Discussion