コンポーネント
React
関数コンポーネント・props・条件レンダリング
関数コンポーネント
props・デフォルト値・children・型定義
import type { ReactNode, MouseEvent } from 'react';
// Props の型定義
interface ButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
className?: string;
}
// 関数コンポーネント
export function Button({
children,
onClick,
variant = 'primary', // デフォルト値
size = 'md',
disabled = false,
className = '',
}: ButtonProps) {
const base = 'rounded font-medium transition-colors';
const variants = {
primary: 'bg-emerald-600 text-white hover:bg-emerald-700',
secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200',
danger: 'bg-red-600 text-white hover:bg-red-700',
};
const sizes = { sm: 'px-3 py-1 text-sm', md: 'px-4 py-2', lg: 'px-6 py-3 text-lg' };
return (
<button
onClick={onClick}
disabled={disabled}
className={`${base} ${variants[variant]} ${sizes[size]} ${className}`}
>
{children}
</button>
);
}条件レンダリングとリスト
&&、三項演算子、map、key
// 条件レンダリング
function UserPanel({ user, isLoading, error }) {
// アーリーリターン(推奨)
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage message={error.message} />;
if (!user) return null;
return (
<div>
{/* && 演算子: 左辺が truthy の時だけ右辺を表示 */}
{user.isAdmin && <AdminBadge />}
{/* 三項演算子 */}
<span>{user.isActive ? '有効' : '無効'}</span>
{/* 複数条件は変数に切り出す */}
{user.role === 'admin' ? <AdminView /> : <UserView />}
</div>
);
}
// リストレンダリング
function UserList({ users }: { users: User[] }) {
return (
<ul>
{users.map(user => (
// key は一意で安定した値を使う(インデックスは最後の手段)
<li key={user.id}>
<UserCard user={user} />
</li>
))}
{users.length === 0 && <li>ユーザーが見つかりません</li>}
</ul>
);
}コンポジション
children・slots・as prop パターン
// children による合成
function Card({ children, className = '' }) {
return <div className={`rounded-xl border p-6 ${className}`}>{children}</div>;
}
function Card.Header({ children }) { return <div className="mb-4 font-bold">{children}</div>; }
function Card.Body({ children }) { return <div>{children}</div>; }
// 使用
<Card>
<Card.Header>タイトル</Card.Header>
<Card.Body>コンテンツ</Card.Body>
</Card>
// 複数スロット(named slots)
function Layout({ header, sidebar, children }) {
return (
<div className="grid grid-cols-[250px_1fr]">
<aside>{sidebar}</aside>
<div>
<header>{header}</header>
<main>{children}</main>
</div>
</div>
);
}
// as prop(ポリモーフィックコンポーネント)
function Text({ as: Component = 'p', children, ...props }) {
return <Component {...props}>{children}</Component>;
}
// <Text as="h1">見出し</Text>
// <Text as="span">インライン</Text>