ユーザインターフェース構築のためのJavaScriptライブラリ
ブラウザがHTMLを解釈して作る、JavaScriptで操作可能なオブジェクト
// 要素の取得と内容変更
document.getElementById('count').textContent = '1';
// CSSクラスの追加
document.querySelector('div').classList.add('highlighted');
// 新しい要素の作成と追加
const newButton = document.createElement('button');
newButton.textContent = 'クリック';
document.body.appendChild(newButton);
// イベントリスナーの追加
newButton.addEventListener('click', handleClick);
const button = document.getElementById('btn');
const counter = document.getElementById('count');
let count = 0;
button.addEventListener('click', () => {
count++;
counter.textContent = count;
// 他の要素も手動で更新
updateHeader(count);
updateFooter(count);
});
<div>
<h1>カウンター</h1>
<span>{count}</span>
<button onClick={handleClick}>
+1
</button>
</div>
「状態が変わったら全体を再描画」という発想の転換
メモリ上にDOMの軽量コピーを保持する仕組み
状態またはpropsの変更
新しい状態に基づいてメモリ上に仮想DOMを構築
前回の仮想DOMと今回の仮想DOMを比較
変更された部分のみ実DOMに反映
<div>
<h1>カウント: {count}</h1>
<button>+1</button>
</div>
{
type: 'div',
children: [
{
type: 'h1',
children: `カウント: ${count}`
},
{
type: 'button',
children: '+1'
}
]
}
普通のHTMLから始めよう
<div class="greeting">
<h1>Hello, World!</h1>
<p>今日はいい天気ですね</p>
</div>
→ JSXならこれをJS関数の中に書ける
function Greeting() {
return (
<div className="greeting">
<h1>Hello, World!</h1>
<p>今日はいい天気ですね</p>
</div>
);
}
className
に注意! class
はJSの予約語だから
JSXはこれにコンパイルされる:
// JSX
<div className="greeting">Hello!</div>
// ↓ コンパイル後
React.createElement(
"div",
{ className: "greeting" },
"Hello!"
);
// ❌ ダメ
return (
<h1>Title</h1>
<p>Content</p>
);
// ✅ OK - Fragment使用
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
// ❌ HTML風だとダメ
<br>
<img src="photo.jpg">
<input type="text">
// ✅ 必ず閉じる
<br />
<img src="photo.jpg" />
<input type="text" />
// ❌ HTML属性名
<div class="container" onclick="handleClick()">
<label for="email">
// ✅ camelCase
<div className="container" onClick={handleClick}>
<label htmlFor="email">
class
→ className
for
→ htmlFor
onclick
→ onClick
{ /* A */ }
return (
<div>
<h1>Title</h1>
<p>Text</p>
</div>
);
{ /* B */ }
return (
<h1>Title</h1>
<p>Text</p>
);
{ /* C */ }
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
<div><h1>Title</h1><p>Text</p></div>
<h1>Title</h1><p>Text</p>
<><h1>Title</h1><p>Text</p></>
{}で動的コンテンツ
function User({ name, email }) {
const greeting = 'こんにちは';
return (
<div>
<h2>{greeting}, {name}さん</h2>
<p>{email}</p>
<p>今年は{new Date().getFullYear()}年です。</p>
</div>
);
}
{}
の中は普通のJavaScript式function UserProfile({ user }) {
return (
<div>
<h1>Welcome {user.name}!</h1>
{user.isAdmin && (
<div className="admin-panel"> {/* trueの時だけ表示 */}
Admin tools
</div>
)}
{user.notifications.length > 0 && <span>You have {user.notifications.length} messages</span>}
</div>
);
}
condition && JSX
function UserProfile({ user, isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<h1>Welcome {user.name}!</h1> {/* trueの場合 */}
) : (
<h1>Please log in</h1> {/* falseの場合 */}
)}
</div>
);
}
condition ? trueの時 : falseの時
JSX:
{user.loggedIn ? (
<h1>Welcome {user.name}!</h1>
) : (
<h1>Please log in</h1>
)}
{user.isAdmin && (
<div className="admin-panel">
Admin tools
</div>
)}
ERB:
<% if user.logged_in? %>
<h1>Welcome <%= user.name %>!</h1>
<% else %>
<h1>Please log in</h1>
<% end %>
<% if user.admin? %>
<div class="admin-panel">
Admin tools
</div>
<% end %>
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
</li>
))}
</ul>
);
}
<TodoList todos={[
{ id: 1, text: "起きます" },
{ id: 2, text: "コーヒーを飲みます" },
]} />
<ul>
<li>起きます</li>
<li>コーヒーを飲みます</li>
</ul>
key
は必須!Reactが効率的に更新するため
リストをレンダリングする時の必須属性
{/* keyがないとリストの順序が変わった時に状態がズレる */}
{todos.map(todo => (
<li>{todo.text}</li>
))}
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
function AdminPanel() {
const user = { name: "Ichigo", isAdmin: false };
return <>{user.isAdmin && <div>Admin Panel</div>}</>;
}
<div>Admin Panel</div>
false
再利用できるUI部品
// 100回書く代わりに
<UserCard user={user1} />
<UserCard user={user2} />
<UserCard user={user3} />
// これがコンポーネント
function Welcome() {
return <h1>Hello, World!</h1>;
}
// 使う時
function App() {
return (
<div>
<Welcome />
<Welcome />
</div>
);
}
// 1. 関数宣言(大文字で始める)
function UserCard() {
// 2. ロジック(普通のJS)
const userName = 'Kurosaki Ichigo';
// 3. JSXを返す(必須)
return (
<div className="user-card">
<h3>{userName}</h3>
</div>
);
}
// 小さいコンポーネント
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>
);
}
コンポーネントにデータを渡す
// 普通の関数
function greet(name, age) {
return `こんにちは、${name}さん(${age}歳)`;
}
// Reactコンポーネント
function Greeting({ name, age }) {
return <p>こんにちは、{name}さん({age}歳)</p>;
}
// 使用
<Greeting name="Kurosaki Ichigo" age={15} />
propsは読み取り専用
方法1: props
オブジェクト
function Greeting(props) {
return <h1>Hello, {props.name}!</h1>;
}
方法2: 分割代入【destructuring】
function Greeting({ name, age }) {
return <h1>Hello, {name}! You are {age}</h1>;
}
方法3: デフォルト値
function Greeting({ name = "Guest", age = 0 }) {
return <h1>Hello, {name}! You are {age}</h1>;
}
// Cardコンポーネント
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}
// 使用方法
<Card title="Profile">
<p>This goes into children</p>
<button>Edit Profile</button>
</Card>
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
);
}
// 使用
function App() {
const handleClick = () => alert('クリック!');
return (
<Button onClick={handleClick}>
クリックして
</Button>
);
}
Reactのcomponent:
<UserCard
name={user.name}
email={user.email}
isAdmin={true} />
Railsのpartial:
<%= render 'user_card',
user: @user,
show_admin: true %>
function Card({ title, children }) {
return (
<div>
<h2>{title}</h2>
{children}
</div>
);
}
<Card title="Profile">
<p>Content here</p>
</Card>
Content here
<div><h2>{title}</h2>Content here</div>
<p>Content here</p>
<h2>Profile</h2>Content here
children
はコンポーネントの開始・終了タグの間の要素
バグと戦うための武器
コンポーネント名が小文字
function welcome() { // 小文字ダメ
return <h1>Hello</h1>;
}
<welcome /> // HTMLタグと判断される
key
属性忘れ
{users.map(user =>
<div>{user.name}</div> // keyがない
)}
class
属性
<div class="container"> // classNameが正しい
return
忘れ
function Header() {
<h1>Title</h1>; // returnがない
}
ブラウザ拡張機能 Reactの状態を可視化
function TodoItem({ todo }) {
console.log('TodoItem rendered:', todo); // 基本のログ
return (
<li>
{console.log('Rendering todo text:', todo.text) || null}
{todo.text}
</li>
);
}
|| null
で戻り値を無効化
今日学んだ内容を実際に使ってみよう!
git switch master
git pull origin main
cd react-lessons/
npm install
npm run dev
<ZanpakutoCard>
コンポーネントを作成してください
zanpakuto
オブジェクトを Props として受け取るname
, owner
, type
, bankai
,
shikaiAbility
, powerLevel
, isReleased
bankai
は存在する場合のみ表示export interface Zanpakuto {
name: string;
owner: string;
type: "melee" | "kido" | "elemental" | "illusion";
bankai?: string;
shikaiAbility: string;
powerLevel: number;
isReleased: boolean;
}
props
を受け取って全てのプロパティを表示する
function ZanpakutoCard({ zanpakuto }: { zanpakuto: Zanpakuto }) {
const { name, owner, type, bankai, shikaiAbility, powerLevel, isReleased } = zanpakuto; // 分割代入でプロパティを展開
return (
<div>
<h2>{name}({owner}の斬魄刀)</h2> {/* JSXでは{}内にJavaScript式を書ける */}
<p>タイプ: {type}</p>
{bankai && <p>卍解: {bankai}</p>} {/* 条件付きレンダリングの&&演算子:左側がtruthyの場合、右側を実行 */}
<p>始解能力: {shikaiAbility}</p>
<p>霊圧レベル: {powerLevel}</p>
<p>解放済み: {isReleased ? "はい" : "いいえ"}</p> {/* 三項演算子 - 条件 ? 真の場合 : 偽の場合 */}
</div>
);
}
React②: useState、イベント処理、フォーム