dev React

React入門:JSX・コンポーネント・Propsを理解する【他言語経験者向け】

Reactの学習を始めて、JSXの書き方、関数コンポーネント、Propsの仕組みまで一通り手を動かしてみました。この記事では、react jsx propsの基本を他言語経験者の視点で整理していきます。

注記: 本記事に掲載しているユーザー名・PC名はダミーに差し替えています。

参照元

得られるもの

React jsx propsの基本を、C/C++やC#の経験者が「自分の知識に紐付けて」理解するための整理記事です。

  • JSXでの条件分岐(三項演算子、&&、オブジェクトマップ)の使い分け
  • mapによるリスト描画とkeyの役割
  • 関数コンポーネントの2つの書き方
  • セマンティックHTMLタグの使いどころ
  • Propsの基本とTypeScriptでの型定義
  • childrenの概念と活用パターン

教材を読むだけでなく、実際にVite + React + TypeScriptのプロジェクトを作成してハンズオンで確認した内容をまとめています。

前提条件

  • Node.js(v20系)がインストール済みであること
  • Vite + React + TypeScript のプロジェクトが作成済みであること
  • VS Codeなどのエディタが使える状態であること

環境構築がまだの方は、教材の Chapter 02「ViteでReact開発環境を作ろう」を参照してください。

JSXでの条件分岐

三項演算子

JSXの中では if 文が使えないため、条件分岐には三項演算子を使います。

const isLoggedIn = true;

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

C/C++の三項演算子と同じ構文なので、すんなり入ってくるはずです。

三項演算子のネスト

複数条件の場合、ネストも可能です。

const role = "admin"; // "admin" | "member" | "guest"

return (
  <p>
    {role === "admin"
      ? "管理者メニュー"
      : role === "member"
        ? "メンバーページ"
        : "ゲスト画面"}
  </p>
);

ただし、3つ以上になると読みづらくなるため、実務では別の方法を使うことが多いです。

オブジェクトマップ(条件が多い場合の代替手段)

条件が増えたら、オブジェクトマップが便利です。C#の Dictionary<TKey, TValue> と同じ感覚で使えます。

const role = "admin";

const roleLabel: Record<string, string> = {
  admin: "管理者メニュー",
  member: "メンバーページ",
  guest: "ゲスト画面",
};

return <p>{roleLabel[role] ?? "不明なロール"}</p>;

Record<string, string> はTypeScriptの組み込み型で、キーの型と値の型を指定する辞書型です。?? はNull合体演算子で、C#の ?? と同じ動作をします。

値の型は string に限らず、Record<string, number>Record<string, JSX.Element> も可能です。

&&(短絡評価)による条件付き表示

「条件を満たすときだけ表示したい」場合は && が使えます。

const title = "カードタイトル";

return (
  <div>
    {title && <h3>{title}</h3>}
  </div>
);

title に値があれば <h3> を表示し、undefinednull"" なら何も表示しません。C/C++の短絡評価と同じ原理です。

三項演算子で書くと {title ? <h3>{title}</h3> : null} となるところの省略形と考えればOKです。

mapによるリスト描画

配列をJSXのリストに変換するには map を使います。

const fruits = ["りんご", "みかん", "ぶどう"];

return (
  <ul>
    {fruits.map((fruit, index) => (
      <li key={index}>{fruit}</li>
    ))}
  </ul>
);

ポイントをいくつか整理します。

map のコールバック第1引数(ここでは fruit)は配列の各要素を受け取る変数名で、名前は自由です。hoge でも item でも動きますが、慣例として fruits.map((fruit) => ...) のように複数形→単数形にすることが多いです。

keyの役割

key は表示結果には影響しませんが、Reactの内部処理に必要なものです。

Reactはリストを再描画するとき、前回と今回のリストを比較して差分を検出します。このとき key を手がかりに「どの要素が追加・削除・移動されたか」を判定します。

制御系に例えると、PLCで複数のタンクを管理するときのタグ名のようなものです。タグ名なしにアドレス順だけで管理すると、途中に1つ挿入したら全部ずれてしまう——それと同じ問題を防ぐための仕組みです。

なお、keyindex を使うのは簡易的な方法で、リストの並び替えや削除がある場合はユニークなIDを使うのがベターです。

関数コンポーネントの書き方

Reactの関数コンポーネントには、主に2つの書き方があります。

// アロー関数スタイル(React界隈で主流)
const Header = () => {
  return (
    <header>ヘッダー</header>
  );
};
export default Header;
// function宣言スタイル(Next.js公式ドキュメント等で多い)
export default function Header() {
  return (
    <header>ヘッダー</header>
  );
}

機能的な差はないので、プロジェクト内で統一されていればどちらでもOKです。

セマンティックHTMLタグ

<header>, <footer>, <main> などのタグは、全部 <div> に置き換えても動作します。ではなぜ使い分けるのかというと、「この部分が何の役割か」を意味的に示すためです。

  • <header> → ページやセクションのヘッダー
  • <footer> → ページやセクションのフッター
  • <main> → ページの主要コンテンツ

アクセシビリティ(スクリーンリーダー対応)、SEO(検索エンジンの構造理解)、コードの可読性の面でメリットがあります。

制御系に例えると、変数名を全部 temp1, temp2, temp3 にしても動くけど、tankLevel, pumpSpeed, valveState にした方が保守しやすい——それと同じ考え方です。

Propsの基本

Propsは、親コンポーネントから子コンポーネントにデータを渡す仕組みです。

TypeScriptでの型定義

教材のサンプルはJavaScript(JSX)前提で書かれているため、型定義がありません。TypeScriptプロジェクトでは、Propsの型を定義するのがベターです。

// interfaceで事前宣言する方法(Propsが多い場合に推奨)
interface GreetingProps {
  name: string;
  message: string;
}

const Greeting = ({ name, message }: GreetingProps) => {
  return (
    <div className="bg-blue-100 p-4 rounded-lg">
      <h2 className="text-xl font-semibold">こんにちは、{name}さん!</h2>
      <p className="text-gray-700">{message}</p>
    </div>
  );
};

export default Greeting;

Propsが1〜2個で簡単なものなら、インラインで直接書くこともできます。

const Greeting = ({ name, message }: { name: string; message: string }) => {

【ハンズオン】Propsとchildrenを実際に試す

ここからは、実際にプロジェクトを作成してハンズオンで確認した内容です。

プロジェクトの作成

mkdir react-tailwind
cd react-tailwind
npm create vite@latest . -- --template react-ts

Tailwind CSSもインストールして設定します。

npm install tailwindcss @tailwindcss/vite

vite.config.ts にTailwindプラグインを追加します。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [react(), tailwindcss()],
})

src/index.css を以下の内容に置き換えます。

@import "tailwindcss";

プロジェクトのフォルダ構成

src/
├── App.css
├── App.tsx
├── assets/
├── Card.tsx
├── CardExample.tsx
├── Greeting.tsx
├── GreetingSection.tsx
├── index.css
└── main.tsx

Propsの動作確認:挨拶コンポーネント

まず、Propsでデータを受け渡す基本的なパターンを確認しました。

src/Greeting.tsx — 子コンポーネント:

interface GreetingProps {
  name: string;
  message: string;
}

const Greeting = ({ name, message }: GreetingProps) => {
  return (
    <div className="bg-blue-100 p-4 rounded-lg text-center">
      <h2 className="text-xl font-semibold">こんにちは、{name}さん!</h2>
      <p className="text-gray-700">{message}</p>
    </div>
  );
};

export default Greeting;

src/GreetingSection.tsx — 親コンポーネント:

import Greeting from "./Greeting";

const GreetingSection = () => {
  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-6 text-center">挨拶コンポーネント</h1>
      <div className="space-y-4">
        <Greeting name="田中太郎" message="今日もプログラミングを頑張りましょう!" />
        <Greeting name="佐藤花子" message="Reactの学習を楽しんでいますか?" />
        <Greeting name="鈴木一郎" message="コンポーネントの再利用は便利ですね。" />
      </div>
    </div>
  );
};

export default GreetingSection;
React Propsで挨拶コンポーネントに異なるデータを渡した実行結果

同じ Greeting コンポーネントに異なるPropsを渡すことで、3つの異なる表示が得られることを確認できました。

childrenの動作確認:カードコンポーネント

次に、childrenを使った「中身を外から差し込む」パターンを確認しました。

childrenとは、コンポーネントの開始タグと閉じタグの間に挟んだものが自動的に渡される特殊なPropsです。

Propsだけでもデータは渡せますが、渡したいものがJSX(HTML構造やコンポーネント)になると、Propsがどんどん増えて管理が大変になります。childrenは「中身の構造を呼び出し側に自由に任せたい」ときに使うものです。

src/Card.tsx — 枠だけのコンポーネント:

const Card = ({ title, children }: { title?: string; children: React.ReactNode }) => {
  return (
    <div className="bg-white rounded-lg shadow-md p-6 m-4">
      {title && (
        <h3 className="text-xl font-semibold mb-4">{title}</h3>
      )}
      <div>
        {children}
      </div>
    </div>
  );
};

export default Card;

title?? はオプショナル(省略可能)の意味です。ここでは && による条件付き表示も使っています。titleに値があれば見出しを表示し、なければ何も表示しません。

src/CardExample.tsx — 中身を自由に差し込む:

import Card from './Card';

const CardExample = () => {
  return (
    <div className="p-8">
      <h1 className="text-2xl font-bold mb-6">Childrenの活用例</h1>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
        <Card title="テキストカード">
          <p className="text-gray-700">
            このカードには通常のテキストが入っています。
            childrenを使うことで、様々な内容を表示できます。
          </p>
        </Card>

        <Card title="リストカード">
          <ul className="list-disc pl-5 text-gray-700">
            <li>項目1</li>
            <li>項目2</li>
            <li>項目3</li>
          </ul>
        </Card>

        <Card title="ボタン付きカード">
          <p className="text-gray-700 mb-4">
            このカードにはボタンが含まれています。
          </p>
          <button className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
            アクション
          </button>
        </Card>

        <Card>
          <div className="text-center">
            <h4 className="text-lg font-medium mb-2">タイトルなしカード</h4>
            <p className="text-gray-600">
              titleプロパティを渡さない場合、タイトル部分は表示されません。
            </p>
          </div>
        </Card>
      </div>
    </div>
  );
};

export default CardExample;
React childrenを使ったカードコンポーネントの実行結果

同じ Card コンポーネントの枠の中に、テキスト・リスト・ボタン・タイトルなしなど、まったく異なる中身を差し込めることが確認できました。

まとめ

今回は、JSXの条件分岐、関数コンポーネント、Propsとchildrenについて学びました。

振り返ると、JSXの条件分岐はC/C++の三項演算子や短絡評価と同じ原理でした。条件が多い場合のオブジェクトマップは Record<string, string> で辞書的に管理でき、C#の Dictionary と同じ感覚です。

Propsは親から子へのデータの受け渡し、childrenは「枠だけ作って中身は外から差し込む」仕組みで、コンポーネントの再利用性を高める設計パターンでした。

TypeScriptプロジェクトでは、interfaceやインライン型でPropsの型を定義しておくことで、エディタの補完やエラー検出の恩恵を受けられます。

それでは、また別の記事でお会いしましょう。最後まで読んでいただきありがとうございました!

これまでの学習内容

この記事で紹介した内容の他にも学習した内容を記事にしています。是非、ご覧になってみて下さい。

React入門 Vite + React + TypeScriptで開発環境を構築する手順をハンズオン形式で解説
Vite React TypeScript setupでTodoAppの開発環境を構築する

Vite React TypeScript setup の手順を、実際のハンズオンを通じてステップバイステップで解説します。この記事は、筆者がReact学習の第一歩として環境構築を行った際の体験をもと ...

続きを見る

react jsx propsの基本を他言語経験者向けに解説する入門記事のアイキャッチ
React入門:JSX・コンポーネント・Propsを理解する【他言語経験者向け】

Reactの学習を始めて、JSXの書き方、関数コンポーネント、Propsの仕組みまで一通り手を動かしてみました。この記事では、react jsx propsの基本を他言語経験者の視点で整理していきます ...

続きを見る

react event handlingの基本をクリック・入力・フォームイベントのハンズオンで解説する入門記事のアイキャッチ
React Event Handling入門:クリック・入力・フォームイベントをハンズオンで学ぶ

React event handlingの基本を、実際にコードを書いて動かしながら学びました。この記事では、クリックイベント、入力イベント、イベントオブジェクト、デフォルト動作の防止まで、ハンズオンで ...

続きを見る

React useState useEffect 状態管理と副作用処理の基本をハンズオンで学ぶ
React useState と useEffect を理解する — 状態管理と副作用の基本

React の useState と useEffect は、コンポーネントの状態管理と副作用処理を担う最も重要な Hook です。この記事では、実際にカウンターやタイマーを作りながら、これらの仕組み ...

続きを見る

React useContext Props drillingを解消する状態共有の仕組みをハンズオンで学ぶ記事のアイキャッチ画像
React useContextで学ぶ状態共有 — Props drillingからの脱却

Reactでコンポーネントの階層が深くなると、「親から子へ、子から孫へ」とPropsをバケツリレーのように渡し続ける場面に出会います。これが Props drilling と呼ばれる厄介な問題です。u ...

続きを見る

-dev, React