React入門完全ガイド!初心者が絶対に理解すべき基本概念とチュートリアル

React入門ガイド始めるよ!

現代のWeb開発において、React(リアクト)は最も人気の高いJavaScriptライブラリの一つです。Facebook(現Meta)が開発したReactは、ユーザーインターフェース(UI)の構築を効率的に行うための強力なツールです。この記事では、React初心者の方でも理解しやすいように、基本概念から実践的な使い方まで、段階的に解説していきます。

Reactを学ぶことで、モダンなWebアプリケーションを構築する能力が身につき、フロントエンド開発者としてのスキルを大幅に向上させることができます。本記事では、実際のコードサンプルを豊富に用いながら、Reactの核となる概念を一つずつ丁寧に説明していきます。

目次

Reactとは

Reactは、Facebookが開発したJavaScriptライブラリで、ユーザーインターフェースの構築に特化しています。従来のWebページ開発とは異なり、Reactでは「コンポーネント」という概念を使って、再利用可能なUI部品を作成します。

Reactの主な特徴

  • コンポーネントベース:UIを小さな部品に分割して管理
  • 仮想DOM:効率的な画面更新を実現
  • 宣言的:「どのような見た目にするか」を記述
  • 再利用性:一度作ったコンポーネントを何度でも使用可能

初心者ポイント:Reactを学ぶことで、従来のDOM操作よりも直感的で保守性の高いWebアプリケーションを開発できるようになります。

コンポーネントの作成とネスト

コンポーネントはReactアプリの“部品”です。ボタンやヘッダー、リストなど、あらゆるUIの要素を小さな単位(コンポーネント)として作り、それらを組み合わせてアプリ全体を組み立てます。

基本的なコンポーネントの作成

function WelcomeMessage() {
  return (
    <div>
      <h2>こんにちは、React の世界へようこそ!</h2>
      <p>これは私の最初のReactコンポーネントです。</p>
    </div>
  );
}
  1. 関数コンポーネントの定義
    • function WelcomeMessage() { ... } の形式で定義されています。これは、React 16.8以降で推奨されている関数コンポーネントの形式です。
    • コンポーネント名は慣習的にパスカルケース(例: WelcomeMessage)で始まります。
  2. JSX (JavaScript XML)
    • return (...) の中に書かれている <div>, <h2>, <p> といったHTMLのような記述は、ReactのJSX (JavaScript XML) と呼ばれる構文です。JavaScriptのコード内でHTMLのような要素を記述することができ、これによりUIの構造を直感的に表現できます。
    • JSXはBabelのようなツールによって最終的に通常のJavaScript(React.createElement()の呼び出し)に変換されます。
  3. 単一のルート要素
    • Reactコンポーネントは、return文で必ず1つのルート要素を返す必要があります。この例では、<div>がそのルート要素となっています。
    • 複数の要素を返したい場合は、<></>Fragmentと呼ばれる短縮構文)を使用することもできますが、このコンポーネントでは単一の<div>で囲んでいます。
  4. 静的コンテンツの表示:
    • このコンポーネントは、外部からのデータを受け取らず、<h2><p>タグ内に固定された文字列を表示しています。

コンポーネントのネスト

コンポーネントは他のコンポーネントの中で使用できます。これをネストと呼びます。
このコード例は、Reactにおけるコンポーネントのネスト(入れ子構造) の概念を示しています。複数の小さなコンポーネントを組み合わせて、より大きなUI(ユーザーインターフェース)を構築する方法が分かります。

function Header() {
  return (
    <header>
      <h1>私のウェブサイト</h1>
      <nav>
        <ul>
          <li>ホーム</li>
          <li>サービス</li>
          <li>お問い合わせ</li>
        </ul>
      </nav>
    </header>
  );
}

function MainContent() {
  return (
    <main>
      <WelcomeMessage /> {/* WelcomeMessage コンポーネントがある前提です */}
      <p>ここにメインコンテンツが表示されます。</p>
    </main>
  );
}

function App() {
  return (
    <div>
      <Header />
      <MainContent />
    </div>
  );
}
  • Appコンポーネントは、Reactアプリケーションの最上位(ルート)コンポーネントとして機能することが多いです。
  • このコンポーネントは、<Header /><MainContent />という2つのコンポーネントを<div>要素の中にネストして呼び出しています。
  • これにより、Headerコンポーネントがレンダリングする内容(ヘッダー)と、MainContentコンポーネントがレンダリングする内容(メインコンテンツと、その中にネストされたWelcomeMessage)が、縦に並んで表示されるウェブページのような構造が構築されます。

初心者ポイント:コンポーネント名は必ず大文字で始めます。HTMLタグは小文字、Reactコンポーネントは大文字で区別されます。

JSXでマークアップを書く

JSXは、JavaScriptの中でHTMLライクな記法を使える構文拡張です。Reactでは、JSXを使ってUIの構造を記述します。

JSXの基本ルール

function ProductCard() {
  const productName = "MacBook Pro";
  const price = 248000;
  const isAvailable = true;

  return (
    <>
      <div className="product-card">
        <h3>{productName}</h3>
        <p>価格: ¥{price.toLocaleString()}</p>
        <p>在庫: {isAvailable ? "あり" : "なし"}</p>
        <img src="/macbook.jpg" alt={productName} />
      </div>
    </>
  );
}
  1. すべてのタグは閉じる必要がある
    • HTMLでは<img><input>のように閉じタグが不要な要素がありますが、JSXではすべての要素に閉じタグが必要です。
    • 閉じタグがない単一の要素(例: <img>)は、<img ... />のように自己終了タグとして記述します。
    • このコードでは、div, h3, p は通常の閉じタグを持ち、img は自己終了タグとして記述されています。
  2. 複数の要素を返すには、親要素で囲む
    • Reactコンポーネントのreturn文は、必ず1つの親要素を返さなければなりません。
    • このProductCardコンポーネントでは、<div className="product-card">という単一のdiv要素が返されています。
    • もし、div要素の他に、例えば別の<span>要素を並列で返したい場合は、それらをさらに別の<div>で囲むか、またはFragmentと呼ばれる短縮構文<></>で囲む必要があります。このコードでは<>...</>が使われていますが、これはdiv要素が一つしかないため、厳密には不要ですが、慣習的に使われることもあります。FragmentはDOMに余計なノードを追加せずに複数の要素をグループ化する際に非常に便利です。
  3. JavaScriptの式は波括弧 {} で囲む
    • JSX内でJavaScriptの変数、関数呼び出し、式などを使いたい場合は、それらを波括弧 {} で囲む必要があります。
    • <h3>{productName}</h3>: productNameというJavaScript変数の値が表示されます。
    • <p>価格: ¥{price.toLocaleString()}</p>: price変数の値をtoLocaleString()メソッドでカンマ区切りに整形した結果が表示されます。

初心者ポイント:JSXでは、HTMLの「class」属性は「className」として記述します。これはJavaScriptの予約語との競合を避けるためです。

JSXでの条件分岐

function UserGreeting({ userName, isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h2>おかえりなさい、{userName}さん!</h2>
      ) : (
        <h2>ゲストユーザーでログインしています</h2>
      )}
    </div>
  );
}
  • JSX内で{...}の中に三項演算子? :)を使い、状況によって表示する内容を切り替えています。
  • 三項演算子は「条件 ? 真のとき : 偽のとき」という形で使います。

スタイルの追加方法

Reactでは、様々な方法でスタイルを適用できます。CSS、インラインスタイル、CSS-in-JSなど、プロジェクトの要件に応じて選択できます。

CSSファイルを使用する方法

// styles.css
.card {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  padding: 20px;
  margin: 10px;
}

.card-title {
  color: #333;
  font-size: 1.5em;
  margin-bottom: 10px;
}

これは一般的なCSSの記述方法です。.card.card-titleという2つのCSSクラスが定義されています。

それぞれのクラスは、背景色、角の丸み、影、余白、文字の色やサイズなど、UI要素の見た目を決定するプロパティを持っています。

これらのスタイルは、HTML要素にこれらのクラス名が付与された際に適用されます。

// React コンポーネント
import './styles.css';

function NewsCard({ title, content, date }) {
  return (
    <div className="card">
      <h3 className="card-title">{title}</h3>
      <p>{content}</p>
      <small>投稿日: {date}</small>
    </div>
  );
}
  1. CSSファイルのインポート
    • import './styles.css'; の行がこのコードの肝です。これにより、定義されたstyles.cssファイルがこのReactコンポーネント(またはアプリケーション全体)に読み込まれます。
    • ビルドツール(WebpackやViteなど)がこのimport文を処理し、CSSルールが最終的なウェブページに適用されるようにします。
  2. CSSクラスの適用
    • div要素には className="card" が、h3要素には className="card-title" がそれぞれ適用されています。
    • HTMLでは class 属性を使いますが、JSXではJavaScriptの予約語との衝突を避けるため、代わりに className を使用します。
    • このclassNamestyles.cssで定義したクラス名を指定することで、それぞれのHTML要素にCSSのスタイルが適用されます。

インラインスタイルを使用する方法

function StatusBadge({ status }) {
  const badgeStyle = {
    padding: '5px 10px',
    borderRadius: '4px',
    fontSize: '0.8em',
    fontWeight: 'bold',
    color: 'white',
    backgroundColor: status === 'active' ? '#28a745' : '#dc3545',
  };

  return (
    <span style={badgeStyle}>
      {status === 'active' ? '有効' : '無効'}
    </span>
  );
}
  1. スタイルを定義するJavaScriptオブジェクト
    • const badgeStyle = { ... }; のように、JavaScriptのオブジェクトとしてスタイルを定義しています。
    • CSSのプロパティ名(例: background-color)は、JavaScriptではキャメルケース(例: backgroundColor)で記述します。値は文字列で指定します。
  2. 条件に応じて動的にスタイルを変更
    • backgroundColor: status === 'active' ? '#28a745' : '#dc3545', の部分が重要です。
    • ここでは三項演算子を使用し、status'active'であれば背景色を緑(#28a745)に、そうでなければ赤(#dc3545)に設定しています。これにより、バッジの視覚的なフィードバックが動的に変わります。
  3. style属性でJavaScriptオブジェクトを渡す
    • <span style={badgeStyle}> のように、HTML要素のstyle属性に、先ほど定義したbadgeStyleというJavaScriptオブジェクトを波括弧 {} で囲んで渡しています。
    • この記法により、JavaScriptで定義したスタイルが直接その要素に適用されます。

初心者ポイント:インラインスタイルはJavaScriptオブジェクトとして記述し、CSSプロパティ名はキャメルケース(backgroundColor)で記述します。

データの表示

Reactでは、JavaScriptの変数やオブジェクトのデータを簡単にUIに表示できます。波括弧 {} を使って、動的なデータを埋め込むことができます。

基本的なデータの表示

function UserProfile() {
  const user = {
    name: "田中太郎",
    age: 28,
    email: "tanaka@example.com",
    avatar: "/avatar.jpg",
    skills: ["JavaScript", "React", "Node.js"],
  };

  return (
    <div className="user-profile">
      <img src={user.avatar} alt={`${user.name}のアバター`} />
      <h2>{user.name}</h2>
      <p>年齢: {user.age}歳</p>
      <p>メール: {user.email}</p>
      <p>スキル: {user.skills.join(', ')}</p>
    </div>
  );
}
  1. JSX内でのオブジェクトプロパティへのアクセス
    • <img src={user.avatar} ... /><h2>{user.name}</h2> のように、JSX内で波括弧 {} を使用して、定義したuserオブジェクトの各プロパティ(例: user.avatar, user.name)にアクセスし、その値をHTML要素内に表示しています。
    • alt属性のように文字列リテラルとJavaScriptの式を組み合わせる場合は、テンプレートリテラル (`${user.name}のアバター`) を使用すると便利です。
  2. 配列データの整形と表示
    • skillsプロパティは["JavaScript", "React", "Node.js"]という配列です。
    • <p>スキル: {user.skills.join(', ')}</p> のように、配列のjoin(', ')メソッドを使って、配列の要素をカンマとスペースで区切った一つの文字列に変換して表示しています。これにより、複数のスキルがきれいに一行で表示されます。

計算結果の表示

function PriceCalculator() {
  const price = 1200;
  const tax = 0.1;
  const quantity = 3;

  return (
    <div>
      <h3>料金計算</h3>
      <p>単価: ¥{price.toLocaleString()}</p>
      <p>数量: {quantity}個</p>
      <p>小計: ¥{(price * quantity).toLocaleString()}</p>
      <p>税込価格: ¥{Math.floor(price * quantity * (1 + tax)).toLocaleString()}</p>
    </div>
  );
}
  1. JavaScriptの計算とJSXへの埋め込み
    • JSX内では波括弧 {} を使うことで、JavaScriptの式を埋め込むことができます。
    • <p>小計: ¥{(price * quantity).toLocaleString()}</p> のように、price * quantityという計算式の結果が直接表示されます。
  2. 数値のフォーマット (toLocaleString())
    • price.toLocaleString()(price * quantity).toLocaleString() のように、toLocaleString()メソッドを使用することで、数値が適切な地域の通貨形式(例: 日本では3桁ごとのカンマ区切り) に変換されて表示されます。これにより、大きな数値でも非常に読みやすくなります。
  3. 小数点以下の処理 (Math.floor())
    • 税込価格の計算では、Math.floor(price * quantity * (1 + tax)) を使用しています。
    • (1 + tax)は税率を含んだ乗数(例: 1.1)を表します。
    • Math.floor()は、計算結果の小数部分を切り捨てて整数に変換します。これにより、価格が円単位で正確に表示されます

初心者ポイント:JSXの波括弧内では、JavaScriptの式を書くことができます。関数呼び出しや計算処理も可能です。

条件付きレンダリング

条件付きレンダリングは、特定の条件に基づいて異なるUIを表示する機能です。Reactでは、JavaScriptの条件文をそのまま使用できます。

三項演算子を使用した条件分岐

function WeatherDisplay({ temperature, isRaining }) {
  return (
    <div className="weather-widget">
      <h3>今日の天気</h3>
      <p>気温: {temperature}°C</p>
      <p>
        {isRaining ? (
          <span style={{ color: 'blue' }}>☔ 雨が降っています</span>
        ) : (
          <span style={{ color: 'orange' }}>☀️ 晴れています</span>
        )}
      </p>
      <p>
        服装:{" "}
        {temperature < 15
          ? "厚手のコート"
          : temperature < 25
          ? "軽いジャケット"
          : "半袖でOK"}
      </p>
    </div>
  );
}

このコードは、Reactで三項演算子(Ternary Operator) を使って、特定の条件に基づいて異なるUI要素やテキストをレンダリングする方法を示しています。これは、if/else文をJSX内で簡潔に表現する非常に一般的なパターンです。

isRaining ? ... : ...: これが三項演算子の基本形です。

  • isRainingtrue の場合、? の直後の部分(青い文字で「☔ 雨が降っています」と表示される<span>)がレンダリングされます。
  • isRainingfalse の場合、: の直後の部分(オレンジ色の文字で「☀️ 晴れています」と表示される<span>)がレンダリングされます。

temperatureの部分では、(気温)に基づいて推奨される服装を決定しています。複数の条件をチェックするために、三項演算子がネスト(入れ子) になっています。

論理演算子を使用した条件表示

function NotificationCenter({ notifications, hasNewMessage }) {
  return (
    <div>
      <h3>通知センター</h3>
      {hasNewMessage && (
        <div className="new-message-alert">
          🔔 新しいメッセージがあります
        </div>
      )}

      {notifications.length > 0 ? (
        <ul>
          {notifications.map(notification => (
            <li key={notification.id}>{notification.message}</li>
          ))}
        </ul>
      ) : (
        <p>通知はありません</p>
      )}
    </div>
  );
}

このNotificationCenterコンポーネントは、Reactで論理AND演算子 (&&)三項演算子を組み合わせて、特定の条件が満たされた場合にのみUI要素を表示する、条件付きレンダリングのパターンを示しています。

  • 論理AND演算子 (&&) を使用した条件表示
    • {hasNewMessage && (...) } の部分がこのパターンです。
    • JavaScriptのルールでは、true && 任意の式 の場合、任意の式 が評価され、その結果が返されます。
    • false && 任意の式 の場合、false が返され、任意の式 は評価されません。

ReactのJSXでは、truefalse は何もレンダリングされないため、この特性を利用して、hasNewMessagetrue の場合のみ、div要素(「🔔 新しいメッセージがあります」のアラート)がレンダリングされます。hasNewMessagefalse の場合は、何も表示されません。

初心者ポイント:&&演算子は、左側の条件がtrueの場合のみ右側の要素を表示します。シンプルな条件表示に便利です。

リストのレンダリング

リストのレンダリングは、配列データを繰り返し表示する機能です。Reactでは、JavaScriptのmap()関数を使って配列を変換し、各要素に対応するJSXを生成します。

基本的なリストの表示

function TodoList() {
  const todos = [
    { id: 1, text: "買い物に行く", completed: false },
    { id: 2, text: "React を学習する", completed: true },
    { id: 3, text: "プロジェクトを完成させる", completed: false },
    { id: 4, text: "友達と映画を見る", completed: false },
  ];

  return (
    <div>
      <h3>今日のTODO</h3>
      <ul>
        {todos.map(todo => (
          <li
            key={todo.id}
            style={{
              textDecoration: todo.completed ? 'line-through' : 'none',
              color: todo.completed ? '#888' : '#333',
            }}
          >
            {todo.completed ? '✅' : '⏳'} {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}
  1. map() メソッドを使ったリストのレンダリング
    • {todos.map(todo => (...))} の部分が、Reactでリストを表示する際の最も標準的な手法です。
    • map()メソッドはJavaScriptの配列メソッドで、配列の各要素に対して指定された関数を実行し、その結果を新しい配列として返します。
    • ここでは、todos配列の各todoオブジェクトに対して、対応する<li>要素(リストアイテム)を生成しています。生成された<li>要素の配列がJSXの<ul>タグ内に埋め込まれることで、リストとして表示されます。
  2. key プロップス (key={todo.id})
    • Reactでリストをレンダリングする際には、各リストアイテム(この場合は<li>)に一意のkeyプロップスを必ず設定する必要があります。
    • keyは、Reactがリストの各要素を識別し、リストの要素が追加、削除、または順序変更された際に、どのアイテムが変更されたかを効率的に追跡するために使われます。これにより、パフォーマンスが向上し、予期せぬ挙動を防ぐことができます。
    • この例では、各TODOオブジェクトのidプロパティがユニークなので、それをkeyとして利用しています。

複雑なリストアイテムの表示

function ProductList() {
  const products = [
    { id: 1, name: "iPhone 14", price: 119800, category: "スマートフォン", inStock: true },
    { id: 2, name: "iPad Air", price: 84800, category: "タブレット", inStock: true },
    { id: 3, name: "MacBook Pro", price: 248000, category: "ノートPC", inStock: false },
    { id: 4, name: "Apple Watch", price: 59800, category: "ウェアラブル", inStock: true },
  ];

  return (
    <div>
      <h3>商品一覧</h3>
      <div className="product-grid">
        {products.map(product => (
          <div key={product.id} className="product-card">
            <h4>{product.name}</h4>
            <p>カテゴリ: {product.category}</p>
            <p>価格: ¥{product.price.toLocaleString()}</p>
            <p>
              在庫:{" "}
              {product.inStock ? (
                <span style={{ color: 'green' }}>あり</span>
              ) : (
                <span style={{ color: 'red' }}>なし</span>
              )}
            </p>
          </div>
        ))}
      </div>
    </div>
  );
}

map()メソッドと条件付きレンダリングを組み合わせて、データのリストから動的なUIを生成する方法を示しています。

  1. map() メソッドによるリストのレンダリング
    • {products.map(product => (...) )} の部分が、Reactで動的なリストを生成しています。
    • products配列の各productオブジェクトに対して関数を実行し、それぞれのproductデータから<div className="product-card">というJSX要素を生成しています。
    • map()メソッドはこれらのproduct-card要素の配列を返し、それが<div className="product-grid">の中に挿入されることで、複数の商品カードが一覧として表示されます。
  2. key プロップス (key={product.id})
    • リスト内の各要素には、Reactが要素を効率的に識別し追跡できるように、一意のkeyプロップスが必要です。ここでは、各商品オブジェクトのユニークなidプロパティがkeyとして利用されています。

初心者ポイント:リストの各要素には必ずkey属性を指定してください。Reactがリストの変更を効率的に処理するために必要です。

イベントハンドリング

イベントハンドリングは、ユーザーの操作(クリック、入力など)に応答する機能です。Reactでは、関数をイベントハンドラーとして定義し、JSXの要素に割り当てます。

基本的なイベントハンドリング

function InteractiveButton() {
  const handleClick = () => {
    alert('ボタンがクリックされました!');
  };

  const handleMouseEnter = () => {
    console.log('マウスが要素に入りました');
  };

  const handleMouseLeave = () => {
    console.log('マウスが要素から出ました');
  };

  return (
    <div>
      <button
        onClick={handleClick}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
        クリックしてください
      </button>
    </div>
  );
}

ユーザーがウェブページ上の要素(この場合はボタン)とインタラクトしたときに、特定のJavaScript関数を実行します。

  1. イベントハンドラ関数の定義
    • handleClick, handleMouseEnter, handleMouseLeave の3つの関数は、それぞれ特定のイベントが発生したときに実行されるロジック(イベントハンドラ)を定義しています。
    • これらの関数は、コンポーネントの内部で定義されており、必要に応じてボタンのイベント属性に割り当てられます。
    • アロー関数 (() => { ... }) を使用することで、関数のコンテキスト(this)の問題を気にすることなく簡潔に記述できます。
  2. JSXのイベント属性とハンドラ関数の紐付け:
    • <button> タグの中に、onClick, onMouseEnter, onMouseLeave といった属性が記述されています。これらはReactにおけるイベント属性です。
    • HTMLのイベント属性(例: onclick="myFunction()")とは異なり、Reactのイベント属性はキャメルケースで記述されます(例: onClick)。
    • これらの属性には、直接JavaScriptのコード(通常は定義済みの関数)を波括弧 {} で囲んで渡します。これにより、特定のイベントが発生した際に、対応するJavaScript関数が呼び出されます。

フォーム入力のハンドリング

function ContactForm() {
  const handleSubmit = (event) => {
    event.preventDefault(); // (1) デフォルトのフォーム送信動作をキャンセル
    const formData = new FormData(event.target); // (2) フォームデータを取得
    const name = formData.get('name');
    const email = formData.get('email');
    const message = formData.get('message');

    alert(`お問い合わせありがとうございます!\n名前: ${name}\nメール: ${email}`);
    // (3) ここで通常はAPIへのデータ送信などの処理を行う
  };

  const handleInputChange = (event) => {
    // (4) 入力フィールドの値が変更されるたびにコンソールにログを出力
    console.log(`${event.target.name}: ${event.target.value}`);
  };

  return (
    <form onSubmit={handleSubmit}> {/* (5) フォームの送信イベントをハンドル */}
      <h3>お問い合わせフォーム</h3>
      <div>
        <label>お名前:</label>
        <input
          type="text"
          name="name"
          onChange={handleInputChange} // (6) 入力変更イベントをハンドル
          required // (7) 必須フィールド
        />
      </div>
      <div>
        <label>メールアドレス:</label>
        <input
          type="email"
          name="email"
          onChange={handleInputChange}
          required
        />
      </div>
      <div>
        <label>メッセージ:</label>
        <textarea
          name="message"
          onChange={handleInputChange}
          required
        ></textarea>
      </div>
      <button type="submit">送信</button> {/* (8) フォーム送信ボタン */}
    </form>
  );
}

ユーザーがフォームに入力したデータを受け取り、それを処理する一連の仕組です。

  1. フォーム送信のハンドリング (handleSubmit)
    • const handleSubmit = (event) => { ... };がフォームが送信されたときに呼び出される関数を定義しています。
    • event.preventDefault();は非常に重要です。HTMLの通常のフォーム送信動作は、ページをリロードしたり、指定されたアクションURLにデータを送信したりします。Reactアプリケーションでは通常、ページの再読み込みを防ぎ、JavaScriptでデータを非同期に処理したいため、このメソッドを呼び出してデフォルトの動作をキャンセルします。
    • const formData = new FormData(event.target);はフォームの送信イベントから、event.target (この場合は<form>要素自身) をFormDataコンストラクタに渡すことで、フォーム内のすべての入力フィールドのデータを簡単に取得できます。
    • formData.get('name')は各<input><textarea>タグに設定されたname属性(例: name="name")を使って、対応する入力値を取得します。
    • アラート表示 では取得したnameemailの値をalert()で表示していますが、実際のアプリケーションでは、ここで取得したデータをサーバーのAPIエンドポイントに送信したり、他の状態管理ロジックに渡したりする処理が行われます。
  2. 入力フィールドの変更ハンドリング (handleInputChange)
    • const handleInputChange = (event) => { ... };は各入力フィールドの値が変更されるたびに呼び出される関数を定義しています。
  3. JSXとイベント属性の紐付け
    • <form onSubmit={handleSubmit}>では<form>要素にはonSubmitというReactのイベント属性があり、フォームが送信されたときにhandleSubmit関数が呼び出されるように設定されています。
    • <input onChange={handleInputChange}><input><textarea>などの入力要素にはonChangeというイベント属性があり、ユーザーが入力値を変更するたびにhandleInputChange関数が呼び出されるように設定されています。これにより、リアルタイムで入力値の変化を検出できます。

初心者ポイント:イベントハンドラー名は「handle」で始めるのが慣習です。また、フォーム送信時はevent.preventDefault()を使ってページリロードを防ぎます。

State管理

State(状態)は、コンポーネントが「覚えておく」データです。useStateフックを使用して、動的に変化するデータを管理できます。

ReactのuseStateフックは、関数コンポーネントに「状態」を持たせるための特別な機能です。これにより、コンポーネント内で変化するデータを管理し、そのデータが変更されたときに自動的にUIを更新できるようになります。

基本的なState管理

import { useState } from 'react';

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

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

  const decrement = () => {
    setCount(count - 1);
  };

  const reset = () => {
    setCount(0);
  };

  return (
    <div>
      <h3>カウンター: {count}</h3>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
      <button onClick={reset}>リセット</button>
    </div>
  );
}

ユーザーの操作に応じてUI上のデータを動的に更新し、それを画面に反映させる方法です。

  1. State変数の宣言
    • const [count, setCount] = useState(0); この行がuseStateフックの最も重要な部分です。
    • useState(0)useStateを呼び出すと、State変数が作られます。引数に渡した0が、このStateの初期値になります。
    • [count, setCount]useState配列を返します。配列の分割代入を使って、以下の2つの要素を取り出しています。
    • countは現在のStateの値を保持する変数です。UIに表示したり、計算に使ったりします。
    • setCountcountの値を更新するためだけの関数です。Stateの値を変更したいときは、必ずこのsetCount関数を使います
  2. Stateの更新とUIの自動再レンダリング
    • setCount(count + 1); incrementdecrementresetといった関数の中で、setCountを使ってcountの値を更新しています。
  3. JSXでのStateの表示とイベントハンドラ
    • <h3>カウンター: {count}</h3> はJSXの中で波括弧{}を使って、State変数countの現在の値を直接表示しています。Stateが更新されると、この表示も自動で変わります。
    • <button onClick={increment}>+1</button>: ボタンのonClickイベントに、先ほど定義したincrement関数を渡しています。ユーザーがボタンをクリックするとincrementが実行され、setCountを通じてStateが更新され、最終的に画面が再レンダリングされるという一連の流れが生まれます。

ポイント:Reactでは、setCountのようなState更新関数が呼び出されると、そのコンポーネント全体が自動的に再レンダリングされます。これにより、countの新しい値が<h3>カウンター: {count}</h3>の部分に反映され、画面上の表示も最新の状態になります。もしcount = count + 1;のように直接countの値を変更しても、Reactは変更を検知できないため、UIは更新されません。

複雑なStateの管理

import { useState } from 'react';

function UserSettings() {
  const [settings, setSettings] = useState({
    theme: 'light',
    language: 'ja',
    notifications: true,
    fontSize: 14,
  });

  const handleThemeChange = (newTheme) => {
    setSettings(prev => ({
      ...prev,
      theme: newTheme
    }));
  };

  const handleNotificationToggle = () => {
    setSettings(prev => ({
      ...prev,
      notifications: !prev.notifications
    }));
  };

  const handleFontSizeChange = (event) => {
    setSettings(prev => ({
      ...prev,
      fontSize: parseInt(event.target.value)
    }));
  };

  return (
    <div>
      <h3>設定</h3>
      <div>
        <label>テーマ: </label>
        <select value={settings.theme} onChange={(e) => handleThemeChange(e.target.value)}>
          <option value="light">ライト</option>
          <option value="dark">ダーク</option>
        </select>
      </div>
      <div>
        <label>
          <input
            type="checkbox"
            checked={settings.notifications}
            onChange={handleNotificationToggle}
          />
          通知を受け取る
        </label>
      </div>
      <div>
        <label>文字サイズ: {settings.fontSize}px</label>
        <input
          type="range"
          min="12"
          max="24"
          value={settings.fontSize}
          onChange={handleFontSizeChange}
        />
      </div>
    </div>
  );
}
  1. オブジェクト形式のState
    • const [settings, setSettings] = useState({ ... }); ここでは、ユーザー設定の複数の項目(themelanguagenotificationsfontSize)を一つのJavaScriptオブジェクトとしてStateに格納しています。このように関連するデータをまとめることで、Stateの管理がしやすくなります。
  2. オブジェクトStateの更新方法 (スプレッド構文 ...prev の利用)
    • setSettings(prev => ({ ...prev, theme: newTheme })); オブジェクト形式のStateを更新する際、Stateの一部だけを変更する場合でも、新しいオブジェクトを完全に作成して返す必要があります。ReactのStateは不変(immutable)であり、既存のオブジェクトを直接変更してはいけません。
    • prev: これはsetSettingsに渡される関数の中で利用できる引数で、更新前のStateの最新の値を指します。
    • ...prev (スプレッド構文)はJavaScriptの記法で、prevオブジェクトのすべてのプロパティと値を展開します。ここではこの構文を使って新しいオブジェクトにコピーします。
    • theme: newThemeでは更新したいプロパティ(ここではtheme)を新しい値で上書きします。 この方法により、languagenotificationsなど、変更しない他の設定プロパティはそのまま保持されつつ、themeだけが更新されるという効率的かつ安全なState更新が実現されます。
  3. 各種入力要素とStateのバインディング
    • <select> (ドロップダウンリスト)
      • value={settings.theme}select要素のvalue属性にStateの値(settings.theme)を設定することで、表示されている選択肢が現在のStateと同期します。
      • onChange={(e) => handleThemeChange(e.target.value)}はユーザーが選択肢を変更するとonChangeイベントが発生し、そのイベントオブジェクトから新しい選択値(e.target.value)を取得してhandleThemeChange関数に渡しています。
    • <input type="checkbox"> (チェックボックス)
      • checked={settings.notifications}はチェックボックスのchecked属性にStateの真偽値(settings.notifications)を設定することで、チェック状態がStateと同期します。
      • onChange={handleNotificationToggle}はユーザーがチェックボックスを操作するとonChangeイベントが発生し、handleNotificationToggleが呼び出されます。この関数内で!prev.notificationsを使ってStateの真偽値を反転させています。
    • <input type="range"> (スライダー):
      • value={settings.fontSize}はスライダーの現在値がStateのsettings.fontSizeと同期します。
      • onChange={handleFontSizeChange}はスライダーが動かされるとonChangeイベントが発生します。event.target.valueは常に文字列なので、parseInt()を使って数値に変換してからStateを更新しています。

初心者ポイント:Stateを更新する際は、常に新しいオブジェクトを作成してください。直接変更(mutation)は避け、スプレッド演算子(…)を活用しましょう。

フックの使用

フックは、関数コンポーネントでReactの機能を「フック」するための特殊な関数です。useState以外にも、様々なフックが用意されています。

useEffectフックの活用

useEffectは、関数コンポーネントで副作用(Side Effects) を実行するためのReactフックです。副作用とは、データ取得、DOMの直接操作、タイマーの設定(setIntervalなど)、イベントリスナーの登録・解除など、Reactのレンダリングとは独立して行われる処理のことです。 useEffect(() => { /* 副作用の処理 */ }, [依存配列]); の形式で使います。

import { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const [isActive, setIsActive] = useState(false);

  useEffect(() => {
    let interval = null;
    if (isActive) {
      interval = setInterval(() => {
        setSeconds(seconds => seconds + 1);
      }, 1000);
    } else if (!isActive && seconds !== 0) {
      clearInterval(interval);
    }
    return () => clearInterval(interval);
  }, [isActive, seconds]);

  const toggle = () => {
    setIsActive(!isActive);
  };

  const reset = () => {
    setSeconds(0);
    setIsActive(false);
  };

  return (
    <div>
      <h3>ストップウォッチ</h3>
      <div>経過時間: {seconds}秒</div>
      <button onClick={toggle}>
        {isActive ? '一時停止' : 'スタート'}
      </button>
      <button onClick={reset}>リセット</button>
    </div>
  );
}
  1. useState によるState管理
    • const [seconds, setSeconds] = useState(0);は経過時間を管理するStateです。
    • const [isActive, setIsActive] = useState(false);ではタイマーが現在動作中であるか(スタートしているか)を管理する真偽値のState。 これらのStateが変更されると、コンポーネントは再レンダリングされ、UIが更新されます。
  2. イベントハンドラ
    • toggle()では isActive Stateを切り替えることで、useEffectが再評価され、タイマーの開始/停止が制御されます。
    • reset()secondsを0に、isActivefalseに設定し、タイマーを初期状態に戻します。setIsActive(false)が呼ばれることでuseEffectが再実行され、既存のタイマーが停止されます。

カスタムフックの作成

このコードはReactのカスタムフック(Custom Hook) の概念と、その具体的な活用例を示しています。useLocalStorageというカスタムフックを作成し、これをNoteAppコンポーネントで使うことで、ブラウザのlocalStorageを使ってデータを永続化するメモアプリを実装しています。

なぜカスタムフックが必要なのか?

Reactの標準フック(useState, useEffectなど)だけでは、特定のロジックを複数のコンポーネントで再利用したり、複雑な状態管理をきれいに抽象化したりするのが難しい場合があります。 カスタムフックは、このような再利用可能なロジックを関数として切り出し、「use」というプレフィックスを付けて定義するものです。これにより、ロジックをコンポーネントから分離し、コードの重複を減らし、可読性と保守性を向上させることができます。

import { useState, useEffect } from 'react'; // useEffectも使用するためインポートが必要です

// カスタムフック: useLocalStorage
function useLocalStorage(key, initialValue) {
  // Stateを初期化する際にlocalStorageから値を読み込む
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      // JSON形式で保存されているため、パースして返す
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      // エラーが発生した場合は初期値を返す
      console.error("Error reading from localStorage:", error); // エラーハンドリングを追加
      return initialValue;
    }
  });

  // setValue関数はStateを更新し、同時にlocalStorageにも保存する
  const setValue = (value) => {
    try {
      // 関数が渡された場合はその関数を実行して新しい値を取得
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      // 値をJSON文字列に変換してlocalStorageに保存
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error('Error saving to localStorage:', error);
    }
  };

  return [storedValue, setValue];
}

// カスタムフックを使用するコンポーネント
function NoteApp() {
  // useLocalStorageフックを使ってnotesの状態と、それを更新する関数を取得
  const [notes, setNotes] = useLocalStorage('notes', []);
  // 新しいメモ入力用のState
  const [inputText, setInputText] = useState('');

  // メモを追加する関数
  const addNote = () => {
    if (inputText.trim()) { // 入力が空でないかチェック
      setNotes([...notes, { id: Date.now(), text: inputText, timestamp: new Date().toLocaleString() }]);
      setInputText(''); // 入力フィールドをクリア
    }
  };

  // メモを削除する関数
  const deleteNote = (id) => {
    setNotes(notes.filter(note => note.id !== id)); // 選択されたID以外のメモでフィルタリング
  };

  return (
    <div>
      <h3>メモアプリ</h3>
      <div>
        <input
          type="text"
          value={inputText}
          onChange={(e) => setInputText(e.target.value)}
          placeholder="メモを入力..."
        />
        <button onClick={addNote}>追加</button>
      </div>
      <ul>
        {notes.map(note => (
          <li key={note.id}>
            <span>{note.text}</span>
            <small> ({note.timestamp})</small>
            <button onClick={() => deleteNote(note.id)}>削除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

useLocalStorage カスタムフックの解説

useLocalStorageフックは、ReactのStateをlocalStorageに自動的に同期させる機能を提供します。

  1. Stateの遅延初期化とlocalStorageからの読み込み
    • useState(() => { ... }); のように、useState関数を渡すことで、Stateの初期化がコンポーネントの初回レンダリング時にのみ実行されるようになります。ここでは、その関数内でwindow.localStorage.getItem(key)を使って、指定されたkeyに対応する値をlocalStorageから読み込んでいます。
  2. State更新とlocalStorageへの書き込みの同期
    • このカスタムフックの重要な機能は、setStoredValueuseStateが返すState更新関数)が呼び出されるたびに、その新しい値をlocalStorageにも保存できることです。
  3. setValue関数を独自に定義し、その中でsetStoredValue(valueToStore)でReactのStateを更新しつつ、window.localStorage.setItem(key, JSON.stringify(valueToStore))localStorageにも保存しています。
    • 関数形式のsetValueのサポートをするためvalue instanceof Function ? value(storedValue) : value; の部分で、setValueに渡される引数が関数であるか(例: setCount(prev => prev + 1)のように)をチェックし、その場合は現在のstoredValueを引数にその関数を実行して、適切な新しい値を取得しています。これにより、useStateset関数が持つ機能(関数形式での更新)をそのまま利用できます。
  4. useStateと同じAPIを返す
    • return [storedValue, setValue]; のように、useStateフックと同じ[現在の値, 更新関数]という形式で値を返しています。これにより、このカスタムフックを使用する側は、useStateと同じ感覚で扱えます。

NoteApp コンポーネントでの useLocalStorage の活用

NoteAppコンポーネントは、作成したuseLocalStorageフックを実際に利用する例です。

  1. カスタムフックの利用
    • const [notes, setNotes] = useLocalStorage('notes', []); この一行で、notesというState変数を宣言し、その初期値をlocalStorageから読み込み、setNotes関数が呼び出されるたびにnotesの値を自動的にlocalStorageに保存する機能を手に入れています。コンポーネントのコードは非常にシンプルに保たれています。
  2. メモの追加と削除
    • addNote: 新しいメモをnotes配列に追加し、入力フィールドをクリアします。setNotesを呼ぶことで、useLocalStorageフックが検知し、localStorageも更新されます。
    • deleteNote: 指定されたidを持つメモを配列からフィルタリングして削除します。こちらもsetNotesを呼ぶことでlocalStorageが更新されます。

初心者ポイント:カスタムフックは、共通のロジックを複数のコンポーネントで再利用するための強力な方法です。フック名は「use」で始めるのが慣習です。

コンポーネント間でのデータ共有

コンポーネント間でデータを共有する方法には、Props(プロパティ)の受け渡しや、State のリフトアップなどがあります。

Propsを使用したデータ共有

Reactアプリケーションで最も基本的なデータ共有の方法である「Props(プロップス)を使用したデータの一方向フロー」を示します。親コンポーネントがStateを管理し、そのStateやStateを更新する関数を子コンポーネントにPropsとして渡すことで、コンポーネント間の連携を実現しています。

import React, { useState } from 'react'; // ReactとuseStateをインポート

// 親コンポーネント
function ShoppingApp() {
  const [cart, setCart] = useState([]); // カートの状態を管理

  // カートに商品を追加する関数
  const addToCart = (product) => {
    setCart(prev => [...prev, product]); // 既存のカートに商品を追加
  };

  // カートから商品を削除する関数
  const removeFromCart = (productId) => {
    setCart(prev => prev.filter(item => item.id !== productId)); // 指定されたIDの商品をフィルタリングして削除
  };

  return (
    <div>
      <h2>オンラインショップ</h2>
      {/* ProductListにaddToCart関数をプロップスとして渡す */}
      <ProductList onAddToCart={addToCart} />
      {/* ShoppingCartにカートのアイテムとremoveFromCart関数をプロップスとして渡す */}
      <ShoppingCart items={cart} onRemoveFromCart={removeFromCart} />
    </div>
  );
}

// 商品リストコンポーネント
function ProductList({ onAddToCart }) { // 親から渡されたonAddToCartプロップスを受け取る
  const products = [
    { id: 1, name: "コーヒー", price: 500 },
    { id: 2, name: "紅茶", price: 400 },
    { id: 3, name: "ジュース", price: 300 }
  ];

  return (
    <div>
      <h3>商品一覧</h3>
      {products.map(product => (
        <div key={product.id}>
          <span>{product.name} - ¥{product.price}</span>
          {/* ボタンクリック時にonAddToCart関数を呼び出し、商品情報を渡す */}
          <button onClick={() => onAddToCart(product)}>
            カートに追加
          </button>
        </div>
      ))}
    </div>
  );
}

// ショッピングカートコンポーネント
function ShoppingCart({ items, onRemoveFromCart }) { // 親から渡されたitemsとonRemoveFromCartプロップスを受け取る
  // カート内の商品の合計金額を計算
  const total = items.reduce((sum, item) => sum + item.price, 0);

  return (
    <div>
      <h3>ショッピングカート</h3>
      {items.length === 0 ? ( // カートが空かどうかで表示を切り替える
        <p>カートは空です</p>
      ) : (
        <> {/* 複数の要素を返すためのFragment */}
          {items.map((item, index) => ( // カート内のアイテムをリスト表示
            <div key={index}> {/* リストのkeyはここではindexを使用 (注: 実際のアプリでは一意なID推奨) */}
              <span>{item.name} - ¥{item.price}</span>
              {/* ボタンクリック時にonRemoveFromCart関数を呼び出し、商品のIDを渡す */}
              <button onClick={() => onRemoveFromCart(item.id)}>
                削除
              </button>
            </div>
          ))}
          <div>合計: ¥{total}</div> {/* 合計金額を表示 */}
        </>
      )}
    </div>
  );
}

このアプリケーションは、以下の3つのコンポーネントで構成されています。

  1. ShoppingApp (親コンポーネント)
    • アプリケーション全体のState(カートの中身)を管理します。
  2. ProductList (子コンポーネント)
    • 表示する商品データのリストを持っています。
  3. ShoppingCart (子コンポーネント)
    • 親から渡された「カート内のアイテムリスト」と「カートから削除する関数」をPropsとして受け取ります。

  1. 単一の真実の情報源 (Single Source of Truth)
    • ShoppingAppコンポーネントが、cartというStateを管理する唯一の場所(真実の情報源)です。カートの中身に関する情報はすべてこの親コンポーネントに集約されます。
    • このように、共有されるStateを最も近い共通の親コンポーネントで管理するのがReactの基本的な思想です。
  2. Propsを通じたデータの一方向フロー
    • 親コンポーネントShoppingAppのStateであるcartは、items={cart}というPropsとしてShoppingCartコンポーネントに「下向き」に渡されます。
    • ShoppingAppで定義されたaddToCart関数とremoveFromCart関数は、それぞれProductListShoppingCartコンポーネントにPropsとして「下向き」に渡されます。
    • 子コンポーネントは受け取った関数を、ユーザーの操作(ボタンクリックなど)があったときに実行します。このとき、子コンポーネントは必要に応じて引数(例: productproductId)を渡します。これにより、子コンポーネントは親コンポーネントのStateを変更するよう「通知」できます。
  3. Stateの不変性 (Immutability)
    • setCart(prev => [...prev, product])setCart(prev => prev.filter(...)) のように、setCartを呼び出す際には、既存のcart配列を直接変更するのではなく、新しい配列を作成して返しています。これはReactのState更新の基本的なルールであり、変更を効率的に検知して再レンダリングを最適化するために重要です。
  4. keyプロップスの重要性
    • products.map(product => (<div key={product.id}>...</div>)) のように、リストをレンダリングする際には、各アイテムに一意なkeyプロップスを与えることが必須です。これによりReactはリストのアイテムを効率的に識別し、パフォーマンスを向上させることができます。ShoppingCartでは簡略化のためindexを使っていますが、これはアイテムが追加・削除・並べ替えされる可能性がある場合には非推奨です。

Context APIを使用した深いデータ共有

ReactのContext APIを使って、コンポーネントツリーの階層を深く掘り下げずに(Propsをバケツリレーのように渡し続けることなく)、データを共有する方法を示します。

なぜContext APIを使うのか?

Reactでコンポーネント間でデータを共有する基本的な方法はPropsを介して親から子へデータを渡すことです。しかし、コンポーネントの階層が深くなると、子のさらに子、そのまた子へとPropsを何層も渡し続けることになります。これを「Props Drilling(プロップス・ドリル)」と呼び、コードが読みにくく、保守しづらくなる原因となります。

Context APIは、このようなProps Drillingの問題を解決するために設計されました。特定のデータをグローバルに利用できる「コンテキスト」 として定義することで、コンポーネントツリー内のどこからでもそのデータにアクセスできるようになります。

import { createContext, useContext, useState } from 'react';

// (1) テーマ用のContextを作成
const ThemeContext = createContext();

// (2) テーマプロバイダーコンポーネント
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light'); // (A) テーマの状態を管理

  // (B) テーマを切り替える関数
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

  return (
    // (C) Context.Provider で value を提供
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children} {/* (D) Providerでラップされた子コンポーネントがここにレンダリングされる */}
    </ThemeContext.Provider>
  );
}

// (3) テーマを使用するコンポーネント (Header)
function Header() {
  // (E) useContextフックを使ってContextから値を取得
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <header
      style={{
        background: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#333' : '#fff',
        padding: '20px'
      }}
    >
      <h1>My App</h1>
      <button onClick={toggleTheme}> {/* (F) Contextから取得したtoggleTheme関数を使用 */}
        {theme === 'light' ? 'ダークモード' : 'ライトモード'}
      </button>
    </header>
  );
}

// (4) テーマを使用するコンポーネント (Content)
function Content() {
  // (G) useContextフックを使ってContextから値を取得
  const { theme } = useContext(ThemeContext);

  return (
    <main
      style={{
        background: theme === 'light' ? '#f5f5f5' : '#666',
        color: theme === 'light' ? '#333' : '#fff',
        padding: '20px'
      }}
    >
      <p>現在のテーマ: {theme}</p>
    </main>
  );
}

// (5) アプリケーションのルート
function App() {
  return (
    // (H) ThemeProvider で Header と Content をラップする
    <ThemeProvider>
      <Header />
      <Content />
    </ThemeProvider>
  );
}
  1. createContext() でContextを作成する
    • const ThemeContext = createContext(); これは、データを「提供」したり「消費」したりするための特別なJavaScriptオブジェクトを作成します。createContext()の引数には、Contextの初期値を設定できますが、通常はnullや空のオブジェクトで構いません(Providerが必ず値を設定するため)。
  2. Provider コンポーネント (ThemeContext.Provider)
    • function ThemeProvider({ children }) { ... } このコンポーネントは、Contextの「供給元」となります。
    • ThemeContext.Providerは作成したContextオブジェクト(ThemeContext)には、.Providerというプロパティがあります。これは、Contextの値を設定するためのコンポーネントです。
    • value={{ theme, toggleTheme }}Providervalueプロップスに、提供したいデータを渡します。ここでは、現在のテーマの状態(theme)とテーマを切り替える関数(toggleTheme)をオブジェクトとして渡しています。
    • {children}ThemeProviderでラップされた子コンポーネントが、この{children}の位置にレンダリングされます。
  3. useContext() フックでContextの値を消費する
    • const { theme, toggleTheme } = useContext(ThemeContext); HeaderコンポーネントとContentコンポーネントでは、useContextフックを使ってContextの値を取得しています。
    • useContext(ThemeContext)は引数に作成したContextオブジェクト(ThemeContext)を渡すことで、最も近い上位のThemeContext.Providerから提供されたvalueにアクセスできます。
    • これにより、HeaderContentといったコンポーネントは、Props Drillingを行うことなく、必要なデータ(theme)や関数(toggleTheme)を直接利用できます。
  4. コンポーネントツリーにおけるProviderの位置
    • Appコンポーネント内で、<ThemeProvider><Header><Content>ラップしている点に注目してください。Contextの恩恵を受けるすべてのコンポーネントは、そのContextのProviderの子孫である必要があります。つまり、HeaderContentThemeProviderの子孫なので、ThemeContextの値にアクセスできるのです。

初心者ポイント:Props は親から子への一方向のデータ流れです。深いネストでのデータ共有にはContext APIが便利ですが、過度に使用すると複雑になるため注意が必要です。

よくある質問(FAQ)

Q1. React 入門に必要な前提知識は何ですか?

A: React 学習を始める前に以下の知識が必要です

  • HTML/CSS の基礎: マークアップとスタイリングの理解
  • JavaScript の基礎: 変数、関数、オブジェクト、配列の操作
  • ES6+ の構文: アロー関数、分割代入、テンプレートリテラルなど

Q2. React コンポーネントを作る際の命名規則はありますか?

A: React コンポーネントの命名には以下のルールがあります

  • 必ず大文字で始める(PascalCase)
  • 分かりやすい名前をつける

Q3. useState と props の違いがわかりません

A: React useState と props の主な違いは以下の通りです

特徴useState(state)props
データの所有者そのコンポーネント親コンポーネント
変更可能性変更可能読み取り専用
用途内部状態の管理コンポーネント間のデータ受け渡し

Q4. リストをレンダーする際に key が必要な理由は?

A: key は React がリストアイテムを効率的に管理するために必要です。アイテムの挿入、削除、並べ替えが発生した場合に、React は key を使って何が変更されたかを把握し、必要最小限の DOM 更新を行います。

まとめと次のステップ

この記事では、React の基本概念から実践的な使い方まで、幅広く解説しました。これらの知識を身につけることで、モダンなWebアプリケーションを構築する基礎が整います。

学習した内容の振り返り

  • コンポーネント:UIを再利用可能な部品として管理
  • JSX:JavaScriptの中でHTMLライクな記法を使用
  • Props:コンポーネント間でデータを受け渡し
  • State:コンポーネント内で動的なデータを管理
  • イベントハンドリング:ユーザーの操作に応答
  • フック:関数コンポーネントでReactの機能を活用
  • 条件分岐とリスト:動的なUI表示を実現

次のステップとして学習すべき内容

  • React Router:シングルページアプリケーション(SPA)のルーティング
  • 状態管理ライブラリ:Redux、Zustand、Jotaiなど
  • APIとの連携:fetch、axios、React Query、SWRなど
  • テスト:Jest、React Testing Library
  • パフォーマンス最適化:memo、useMemo、useCallback
  • TypeScript:型安全なReact開発
  • Next.js:Reactベースのフレームワーク

実践的な学習アドバイス

理論だけでなく、実際に手を動かしてアプリケーションを作成することが重要です。簡単なプロジェクトから始めて、徐々に複雑な機能を追加していくことで、Reactの理解が深まります。

また、React の公式ドキュメントやコミュニティの情報を積極的に活用し、最新の開発トレンドやベストプラクティスを学び続けることが、スキル向上の鍵となります。

最後に:React の学習は継続的なプロセスです。基礎をしっかりと身につけた上で、実際のプロジェクトに取り組み、エラーを解決しながら経験を積んでいくことが最も効果的な学習方法です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次