Reactの基礎から:21の例でWebサイトを作る
Reactは対話的なWebページを作るためのJavaScriptライブラリです。ページを「コンポーネント」と呼ばれる小さな部品に分けます。各コンポーネントはデータを受け取り、ページの一部を返します。この記事では、テキストを一つ表示するところから、メニュー、データから作るカード、フッターを備えた小さなWebサイトを組み立てるまでを扱います。すべての例は実際に実行したファイルで、示す出力も実際の出力です。
必要なもの
Node.js 18以降、JavaScriptの基礎、任意のエディタが必要です。npm install [email protected] [email protected] でパッケージを入れ、この記事のテストランナーで各例を実行します。steps を持つ例は、jsdomで作ったブラウザ上でボタンのクリックを再現します。
図解
例 1: 最初のテキストを表示する
コンポーネントはJSXを返す関数です。この例では見出しを作り、renderToStaticMarkupでHTMLに変換します。
import React from "react";
export function App() {
return <h1>สวัสดี React</h1>;
}
**実際の出力**
<h1>สวัสดี React</h1>
例 2: mapでリストを表示する
mapで配列を項目ごとのliに変換します。データが変わるとHTMLを書き換えなくても画面が追従します。
import React from "react";
const fruits = ["ทุเรียน", "มังคุด", "มะม่วง"];
export function App() {
return (
<ul>
{fruits.map(f => <li key={f}>{f}</li>)}
</ul>
);
}
**実際の出力**
<ul><li>ทุเรียน</li><li>มังคุด</li><li>มะม่วง</li></ul>
例 3: コンポーネントにpropsを渡す
propsは親から子へ渡すデータです。同じコンポーネントを異なるデータで再利用できます。
import React from "react";
function Greeting({ name, age }) {
return <p>สวัสดี {name} อายุ {age} ปี</p>;
}
export function App() {
return (
<div>
<Greeting name="สมชาย" age={20} />
<Greeting name="สมหญิง" age={22} />
</div>
);
}
**実際の出力**
<div><p>สวัสดี สมชาย อายุ 20 ปี</p><p>สวัสดี สมหญิง อายุ 22 ปี</p></div>
例 4: 条件によって表示を変える
&& と三項演算子で、画面に何を表示するかを選びます。
import React from "react";
function Status({ online }) {
return (
<div>
{online ? <span>ออนไลน์</span> : <span>ออฟไลน์</span>}
{online && <em>พร้อมแชท</em>}
</div>
);
}
export function App() {
return <Status online={true} />;
}
**実際の出力**
<div><span>ออนไลน์</span><em>พร้อมแชท</em></div>
例 5: Fragment
余分なdivが不要なときは、<> ... </> で複数の要素をまとめます。
import React from "react";
function Header() {
return (
<>
<h2>หัวข้อ</h2>
<p>ไม่ต้องครอบด้วย div</p>
</>
);
}
export function App() {
return <Header />;
}
**実際の出力**
<h2>หัวข้อ</h2><p>ไม่ต้องครอบด้วย div</p>
例 6: テーブルのkey
データからテーブルを作り、idをkeyに使ってReactが各行を識別できるようにします。
import React from "react";
const students = [
{ id: 1, name: "สมชาย", score: 85 },
{ id: 2, name: "สมหญิง", score: 72 },
];
export function App() {
return (
<table>
<tbody>
{students.map(s => (
<tr key={s.id}>
<td>{s.name}</td>
<td>{s.score}</td>
</tr>
))}
</tbody>
</table>
);
}
**実際の出力**
<table><tbody><tr><td>สมชาย</td><td>85</td></tr><tr><td>สมหญิง</td><td>72</td></tr></tbody></table>
例 7: styleはオブジェクトで書く
Reactのstyleはcamel caseのオブジェクトを受け取ります。例:backgroundColor、borderRadius。
import React from "react";
const box = {
padding: 12,
backgroundColor: "#eef4ff",
borderRadius: 8,
color: "#1a1a1a",
};
export function App() {
return <div style={box}>กล่องที่ตกแต่งด้วย style object</div>;
}
**実際の出力**
<div style="padding:12px;background-color:#eef4ff;border-radius:8px;color:#1a1a1a">กล่องที่ตกแต่งด้วย style object</div>
例 8: children
開始タグと終了タグの間にJSXを置くと、コンポーネントは任意の内容を包めます。
import React from "react";
function Card({ title, children }) {
return (
<section>
<h3>{title}</h3>
<div>{children}</div>
</section>
);
}
export function App() {
return (
<Card title="ข่าวสาร">
<p>เนื้อหาที่ส่งเข้าไปใน children</p>
</Card>
);
}
**実際の出力**
<section><h3>ข่าวสาร</h3><div><p>เนื้อหาที่ส่งเข้าไปใน children</p></div></section>
例 9: stateによるカウンター
useStateは変化する値を保持します。setterを呼ぶとReactが再描画します。この例ではボタンを2回押して変化を確認します。
import React, { useState } from "react";
export function App() {
const [count, setCount] = useState(0);
return (
<div>
<p data-out>นับ: {count}</p>
<button id="inc" onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
export const steps = ["#inc", "#inc"];
**実際の出力**
ข้อความเริ่มต้น: นับ: 0
หลังคลิกครั้งที่ 1: นับ: 1
หลังคลิกครั้งที่ 2: นับ: 2
例 10: 制御されたinput
inputの値はstateで制御され、入力のたびにonChangeで更新されます。この例では初期値の0文字を表示します。
import React, { useState } from "react";
export function App() {
const [text, setText] = useState("");
return (
<div>
<input id="box" value={text} onChange={e => setText(e.target.value)} />
<p>พิมพ์แล้ว: {text.length} ตัวอักษร</p>
</div>
);
}
**実際の出力**
<div><input id="box" value=""/><p>พิมพ์แล้ว: 0 ตัวอักษร</p></div>
例 11: オン・オフのスイッチ
booleanをstateに保存し、クリックのたびに !on で反転させます。
import React, { useState } from "react";
export function App() {
const [on, setOn] = useState(false);
return (
<div>
<p data-out>{on ? "เปิดอยู่" : "ปิดอยู่"}</p>
<button id="toggle" onClick={() => setOn(!on)}>สลับ</button>
</div>
);
}
export const steps = ["#toggle", "#toggle"];
**実際の出力**
ข้อความเริ่มต้น: ปิดอยู่
หลังคลิกครั้งที่ 1: เปิดอยู่
หลังคลิกครั้งที่ 2: ปิดอยู่
例 12: useEffect
effectは描画の後に実行されます。データの読み込みや、コンポーネントが最初に表示されたときの準備に使います。
import React, { useEffect, useState } from "react";
export function App() {
const [message, setMessage] = useState("กำลังโหลด");
useEffect(() => {
setMessage("โหลดเสร็จแล้ว (จาก useEffect)");
}, []);
return <p data-out>{message}</p>;
}
export const steps = [];
**実際の出力**
ข้อความเริ่มต้น: โหลดเสร็จแล้ว (จาก useEffect)
例 13: 値を保存するフォーム
inputの値をstateに読み込み、ボタンを押したときに保存します。この例では初期値を「สมชาย」にしています。
import React, { useState } from "react";
export function App() {
const [name, setName] = useState("สมชาย");
const [saved, setSaved] = useState("");
return (
<div>
<input value={name} onChange={e => setName(e.target.value)} />
<button id="save" onClick={() => setSaved(name)}>บันทึก</button>
<p data-out>บันทึกแล้ว: {saved || "ยังไม่มี"}</p>
</div>
);
}
export const steps = ["#save"];
**実際の出力**
ข้อความเริ่มต้น: บันทึกแล้ว: ยังไม่มี
หลังคลิกครั้งที่ 1: บันทึกแล้ว: สมชาย
例 14: ToDoリスト
spreadで新しい配列を作って項目を追加します。元の配列を直接変更しません。
import React, { useState } from "react";
let nextId = 1;
export function App() {
const [items, setItems] = useState([]);
const add = () => {
const id = nextId++;
setItems([...items, { id, text: "งานที่ " + id }]);
};
return (
<div>
<button id="add" onClick={add}>เพิ่มงาน</button>
<ul data-out>{items.map(i => <li key={i.id}>{i.text}</li>)}</ul>
</div>
);
}
export const steps = ["#add", "#add"];
**実際の出力**
ข้อความเริ่มต้น:
หลังคลิกครั้งที่ 1: งานที่ 1
หลังคลิกครั้งที่ 2: งานที่ 1งานที่ 2
例 15: Context
propsを何段も渡さずに、深いところにあるコンポーネントへテーマの値を渡します。
import React, { createContext, useContext } from "react";
const ThemeContext = createContext("light");
function Button() {
const theme = useContext(ThemeContext);
return <button>ธีม: {theme}</button>;
}
export function App() {
return (
<ThemeContext.Provider value="dark">
<Button />
</ThemeContext.Provider>
);
}
**実際の出力**
<button>ธีม: dark</button>
例 16: useReducer
actionとreducerを使い、複数の値や操作を持つstateを管理します。
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "inc": return { count: state.count + 1 };
case "dec": return { count: state.count - 1 };
default: return state;
}
}
export function App() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<p data-out>ค่า: {state.count}</p>
<button id="inc" onClick={() => dispatch({ type: "inc" })}>+</button>
<button id="dec" onClick={() => dispatch({ type: "dec" })}>-</button>
</div>
);
}
export const steps = ["#inc", "#inc", "#dec"];
**実際の出力**
ข้อความเริ่มต้น: ค่า: 0
หลังคลิกครั้งที่ 1: ค่า: 1
หลังคลิกครั้งที่ 2: ค่า: 2
หลังคลิกครั้งที่ 3: ค่า: 1
例 17: カスタムフック
stateのロジックをuseで始まる関数にまとめ、再利用できるようにします。
import React, { useState } from "react";
function useCounter(start) {
const [count, setCount] = useState(start);
return { count, increment: () => setCount(c => c + 1) };
}
export function App() {
const { count, increment } = useCounter(10);
return (
<div>
<p data-out>ค่าตอนนี้: {count}</p>
<button id="inc" onClick={increment}>เพิ่ม</button>
</div>
);
}
export const steps = ["#inc"];
**実際の出力**
ข้อความเริ่มต้น: ค่าตอนนี้: 10
หลังคลิกครั้งที่ 1: ค่าตอนนี้: 11
例 18: useMemo
重い計算の結果を覚えておき、依存する値が変わったときだけ計算し直します。
import React, { useMemo } from "react";
const numbers = [1, 2, 3, 4, 5];
export function App() {
const total = useMemo(() => numbers.reduce((a, b) => a + b, 0), []);
return <p>ผลรวม = {total}</p>;
}
**実際の出力**
<p>ผลรวม = 15</p>
例 19: タブ
ユーザーが押したタブに応じて、objectから内容を選びます。この例ではタブAとBを切り替えます。
import React, { useState } from "react";
const content = {
a: "เนื้อหาแท็บ A",
b: "เนื้อหาแท็บ B",
};
export function App() {
const [tab, setTab] = useState("a");
return (
<div>
<button id="tab-a" onClick={() => setTab("a")}>แท็บ A</button>
<button id="tab-b" onClick={() => setTab("b")}>แท็บ B</button>
<p data-out>{content[tab]}</p>
</div>
);
}
export const steps = ["#tab-b", "#tab-a"];
**実際の出力**
ข้อความเริ่มต้น: เนื้อหาแท็บ A
หลังคลิกครั้งที่ 1: เนื้อหาแท็บ B
หลังคลิกครั้งที่ 2: เนื้อหาแท็บ A
例 20: データの読み込み
コンポーネントが最初に表示されたときにデータを読み込み、読み込み中の表示を出し、完了後にリストを表示します。
import React, { useEffect, useState } from "react";
const DATA = ["สมชาย", "สมหญิง", "สมศักดิ์"];
export function App() {
const [loading, setLoading] = useState(true);
const [items, setItems] = useState([]);
const load = () => {
setLoading(true);
setTimeout(() => { setItems(DATA); setLoading(false); }, 0);
};
useEffect(() => { load(); }, []);
return (
<div>
<button id="reload" onClick={load}>โหลดใหม่</button>
<div data-out>{loading ? <p>กำลังโหลด...</p> : <ul>{items.map(n => <li key={n}>{n}</li>)}</ul>}</div>
</div>
);
}
export const steps = ["#reload"];
**実際の出力**
ข้อความเริ่มต้น: กำลังโหลด...
หลังคลิกครั้งที่ 1: สมชายสมหญิงสมศักดิ์
例 21: コンポーネントで作る小さなWebサイト
ヘッダーとメニュー、紹介の部分、データから作るサービスカード、フッターを一つのページにまとめます。
import React from "react";
const menu = ["หน้าแรก", "บริการ", "ติดต่อ"];
const services = [
{ id: 1, name: "เว็บไซต์องค์กร", price: 15000 },
{ id: 2, name: "ร้านค้าออนไลน์", price: 25000 },
];
function Header() {
return (
<header style={{ background: "#1a2b4c", color: "#fff", padding: "12px" }}>
<strong>บริษัทตัวอย่าง</strong>
<nav>{menu.map(m => <a key={m} href="#" style={{ marginLeft: 12, color: "#fff" }}>{m}</a>)}</nav>
</header>
);
}
function Hero({ title, subtitle }) {
return (
<section>
<h1>{title}</h1>
<p>{subtitle}</p>
</section>
);
}
function ServiceCard({ name, price }) {
return (
<div style={{ border: "1px solid #ccc", borderRadius: 8, padding: 12, margin: 8 }}>
<h3>{name}</h3>
<p>ราคาเริ่มต้น {price.toLocaleString("en-US")} บาท</p>
</div>
);
}
function Footer() {
return <footer><small>© 2026 บริษัทตัวอย่าง</small></footer>;
}
export function App() {
return (
<div>
<Header />
<main>
<Hero title="ยินดีต้อนรับ" subtitle="เราสร้างเว็บให้ธุรกิจของคุณ" />
<div>{services.map(s => <ServiceCard key={s.id} name={s.name} price={s.price} />)}</div>
</main>
<Footer />
</div>
);
}
**実際の出力**
<div><header style="background:#1a2b4c;color:#fff;padding:12px"><strong>บริษัทตัวอย่าง</strong><nav><a href="#" style="margin-left:12px;color:#fff">หน้าแรก</a><a href="#" style="margin-left:12px;color:#fff">บริการ</a><a href="#" style="margin-left:12px;color:#fff">ติดต่อ</a></nav></header><main><section><h1>ยินดีต้อนรับ</h1><p>เราสร้างเว็บให้ธุรกิจของคุณ</p></section><div><div style="border:1px solid #ccc;border-radius:8px;padding:12px;margin:8px"><h3>เว็บไซต์องค์กร</h3><p>ราคาเริ่มต้น 15,000 บาท</p></div><div style="border:1px solid #ccc;border-radius:8px;padding:12px;margin:8px"><h3>ร้านค้าออนไลน์</h3><p>ราคาเริ่มต้น 25,000 บาท</p></div></div></main><footer><small>© 2026 บริษัทตัวอย่าง</small></footer></div>
次のステップ
次はReact Routerでページを分け、fetchで実際のAPIを呼び出し、ViteやNext.jsでプロジェクトを作成しましょう。stateとpropsが分かれば、機能の追加はコンポーネントを組み合わせる作業になります。
まとめ
Reactは、propsを受け取りstateを持つコンポーネントからページを作ります。stateが変わるとReactは影響を受ける部分だけを描き直します。21の例は表示の基本から小さなWebサイト全体の組み立てまでを扱い、この記事のすべての出力は実際の実行結果です。