
条件レンダリング!
Reactアプリケーションを開発していると、特定の条件に応じて表示内容を変更したい場面が頻繁に発生します。例えば、ユーザーのログイン状態によって異なるメニューを表示したり、データの読み込み状況に応じてローディング画面を出したりする場面です。
本記事では、React初学者の方を対象に、条件レンダリングの基本概念から実用的な応用例まで、段階的にわかりやすく解説していきます。
条件レンダーとは
React開発において、条件レンダー(条件付きレンダリング)は非常に重要な概念です。これは、特定の条件に基づいて異なるUI要素を表示したり、要素の表示・非表示を切り替えたりする技術のことを指します。
例えば、以下のような場面で条件レンダーが活用されます:
- ユーザーのログイン状態に応じて異なるメニューを表示する
- データの読み込み中にローディングスピナーを表示する
- エラーが発生した場合にエラーメッセージを表示する
- ユーザーの権限に応じて特定の機能を表示・非表示にする
Reactでは、JavaScriptの標準的な条件分岐構文を使用して条件レンダーを実装できます。これにより、動的で柔軟なユーザーインターフェースを構築することが可能になります。
if文を使った基本的な条件レンダー
最も基本的な条件レンダーの方法は、JavaScript の if
文を使用することです。この方法は理解しやすく、複雑な条件ロジックにも対応できます。
以下は、ユーザーの認証状態に応じて異なるコンテンツを表示する例です:
function UserGreeting({ isLoggedIn, userName }) {
if (isLoggedIn) {
return (
<div>
<h2>おかえりなさい、{userName}さん!</h2>
<p>今日も素晴らしい一日をお過ごしください。</p>
</div>
);
}
return (
<div>
<h2>こんにちは、ゲストさん</h2>
<p>アカウントにログインして、より多くの機能をご利用ください。</p>
</div>
);
}
function App() {
return (
<div>
<UserGreeting isLoggedIn={true} userName="田中太郎" />
<UserGreeting isLoggedIn={false} />
</div>
);
}
この例では、isLoggedIn
プロパティの真偽値に基づいて、全く異なるJSX要素を返しています。条件が true
の場合はウェルカムメッセージを、false
の場合はログインを促すメッセージを表示します。
ポイント: if文を使った条件レンダーは、条件によって大きく異なるコンポーネント構造を返す場合に適しています。コードが読みやすく、デバッグも容易です。
nullを返して要素を非表示にする方法
特定の条件下で何も表示したくない場合があります。そのような場合、Reactコンポーネントから null
を返すことで、要素を完全に非表示にできます。
以下は、重要度に応じて通知を表示・非表示する例です:
function NotificationBanner({ message, priority, showNotification }) {
// 通知を表示しない場合は null を返す
if (!showNotification) {
return null;
}
// 優先度が低い場合も何も表示しない
if (priority === 'low') {
return null;
}
return (
<div className={`notification ${priority}`}>
<span>⚠️ {message}</span>
</div>
);
}
function App() {
return (
<div>
<h1>アプリケーション</h1>
<NotificationBanner
message="システムメンテナンス予定のお知らせ"
priority="high"
showNotification={true}
/>
<NotificationBanner
message="一般的な情報"
priority="low"
showNotification={true}
/>
<p>メインコンテンツがここに表示されます。</p>
</div>
);
}
注意: コンポーネントから null
を返すことは有効ですが、多用すると親コンポーネント側でそのコンポーネントの表示状態が把握しにくくなる場合があります。設計時に十分検討しましょう。
三項演算子を使った効率的な条件レンダー
三項演算子(条件 ? 真の場合 : 偽の場合
)は、条件レンダーにおいて最も頻繁に使用される手法の一つです。簡潔な記述で条件分岐を表現できるため、小さな差異のある要素を切り替える際に非常に便利です。
function ProductCard({ product, isOnSale, discount }) {
return (
<div className="product-card">
<img src={product.imageUrl} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">
{isOnSale ? (
<>
<span className="original-price">¥{product.price}</span>
<span className="sale-price">¥{product.price - discount}</span>
</>
) : (
<span>¥{product.price}</span>
)}
</p>
<div className="status">
{product.inStock ? (
<span className="in-stock">✅ 在庫あり</span>
) : (
<span className="out-of-stock">❌ 在庫切れ</span>
)}
</div>
</div>
);
}
function App() {
const sampleProduct = {
name: 'プログラミング入門書',
price: 3000,
imageUrl: '/book-cover.jpg',
inStock: true
};
return (
<div>
<ProductCard
product={sampleProduct}
isOnSale={true}
discount={500}
/>
</div>
);
}
上記の例では、複数の箇所で三項演算子を使用しています:
- セール中かどうかで価格表示を切り替え
- 在庫状況によってステータス表示を変更
使い分けのポイント: 三項演算子は、条件による差異が小さく、JSX内で直接判定したい場合に適しています。ただし、ネストが深くなりすぎると可読性が下がるため注意が必要です。
論理AND演算子(&&)の活用方法
論理AND演算子(&&)は、「条件が真の場合のみ要素を表示し、そうでなければ何も表示しない」という場面で非常に便利です。三項演算子よりもさらに簡潔に記述できます。
function Dashboard({ user, notifications, hasUnreadMessages }) {
return (
<div className="dashboard">
<header>
<h1>ダッシュボード</h1>
{user.isAdmin && (
<button className="admin-panel-btn">
管理者パネル
</button>
)}
</header>
{hasUnreadMessages && (
<div className="message-alert">
📬 未読メッセージがあります
</div>
)}
{notifications.length > 0 && (
<div className="notifications">
<h2>通知 ({notifications.length}件)</h2>
{notifications.map(notification => (
<div key={notification.id} className="notification-item">
{notification.message}
</div>
))}
</div>
)}
{user.isPremium && (
<div className="premium-features">
<h2>🌟 プレミアム機能</h2>
<p>高度な分析ツールをご利用いただけます。</p>
</div>
)}
</div>
);
}
function App() {
const userData = {
name: '山田花子',
isAdmin: true,
isPremium: false
};
const userNotifications = [
{ id: 1, message: 'システムアップデートのお知らせ' },
{ id: 2, message: 'セキュリティ設定の確認をお願いします' }
];
return (
<Dashboard
user={userData}
notifications={userNotifications}
hasUnreadMessages={true}
/>
);
}
重要な注意点: &&
演算子を使用する際、左辺に数値を直接配置すると予期しない結果になることがあります。例えば {count && <div>{count}</div>}
で、count
が 0
の場合、0
がそのまま画面に表示されてしまいます。{count > 0 && <div>{count}</div>}
のように明示的に真偽値で評価しましょう。
変数への条件付き代入パターン
複雑な条件ロジックやJSX構造を扱う場合、変数に条件付きでJSXを代入するパターンが有効です。この方法により、コードの可読性が向上し、デバッグも容易になります。
function WeatherWidget({ weather, location, isLoading, error }) {
// ローディング状態の処理
if (isLoading) {
return (
<div className="weather-widget loading">
<div className="spinner">🔄</div>
<p>天気情報を取得中...</p>
</div>
);
}
// エラー状態の処理
if (error) {
return (
<div className="weather-widget error">
<div className="error-icon">❌</div>
<p>天気情報の取得に失敗しました</p>
<small>{error.message}</small>
</div>
);
}
// 天気アイコンの決定
let weatherIcon = '☀️';
let backgroundClass = 'sunny';
if (weather.condition === 'rainy') {
weatherIcon = '🌧️';
backgroundClass = 'rainy';
} else if (weather.condition === 'cloudy') {
weatherIcon = '☁️';
backgroundClass = 'cloudy';
} else if (weather.condition === 'snowy') {
weatherIcon = '❄️';
backgroundClass = 'snowy';
}
// 温度に基づいた警告メッセージ
let temperatureWarning = null;
if (weather.temperature > 35) {
temperatureWarning = (
<div className="warning high-temp">
🌡️ 熱中症にご注意ください
</div>
);
} else if (weather.temperature < 0) {
temperatureWarning = (
<div className="warning low-temp">
🧊 路面凍結にご注意ください
</div>
);
}
return (
<div className={`weather-widget ${backgroundClass}`}>
<div className="location">
📍 {location}
</div>
<div className="weather-main">
<div className="weather-icon">{weatherIcon}</div>
<div className="temperature">{weather.temperature}°C</div>
<div className="condition">{weather.description}</div>
</div>
{temperatureWarning}
<div className="details">
<div>湿度: {weather.humidity}%</div>
<div>風速: {weather.windSpeed}m/s</div>
</div>
</div>
);
}
function App() {
const weatherData = {
condition: 'rainy',
temperature: 22,
description: '小雨',
humidity: 80,
windSpeed: 3.2
};
return (
<div>
<WeatherWidget
weather={weatherData}
location="東京都"
isLoading={false}
error={null}
/>
</div>
);
}
この例では、以下の条件レンダーテクニックを組み合わせています:
- 早期リターン: ローディングやエラー状態での早期リターンによる処理の簡素化
- 変数代入: 条件に基づいた値の事前計算と代入
- 条件付きJSX: 必要に応じてJSX要素を変数に代入
- 複合条件: 複数の条件を組み合わせた複雑な分岐処理
実践的な例とベストプラクティス
フォームバリデーションでの条件レンダー
実際のアプリケーションでよく使用される、フォームバリデーションと条件レンダーを組み合わせた例を見てみましょう
function ContactForm({ onSubmit }) {
const [formData, setFormData] = useState({
name: '',
email: '',
message: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitSuccess, setSubmitSuccess] = useState(false);
const validateForm = () => {
const newErrors = {};
if (!formData.name.trim()) {
newErrors.name = 'お名前は必須項目です';
}
if (!formData.email.trim()) {
newErrors.email = 'メールアドレスは必須項目です';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = '有効なメールアドレスを入力してください';
}
if (!formData.message.trim()) {
newErrors.message = 'メッセージは必須項目です';
} else if (formData.message.length < 10) {
newErrors.message = 'メッセージは10文字以上で入力してください';
}
return newErrors;
};
// 送信成功画面
if (submitSuccess) {
return (
<div className="form-success">
<div className="success-icon">✅</div>
<h2>送信完了</h2>
<p>お問い合わせありがとうございました。<br/>
24時間以内にご回答いたします。</p>
</div>
);
}
return (
<form className="contact-form">
<h2>お問い合わせフォーム</h2>
<div className="form-group">
<label htmlFor="name">お名前 *</label>
<input
type="text"
id="name"
value={formData.name}
className={errors.name ? 'error' : ''}
/>
{errors.name && (
<span className="error-message">{errors.name}</span>
)}
</div>
<div className="form-group">
<label htmlFor="email">メールアドレス *</label>
<input
type="email"
id="email"
value={formData.email}
className={errors.email ? 'error' : ''}
/>
{errors.email && (
<span className="error-message">{errors.email}</span>
)}
</div>
<div className="form-group">
<label htmlFor="message">メッセージ *</label>
<textarea
id="message"
rows="5"
value={formData.message}
className={errors.message ? 'error' : ''}
/>
{errors.message && (
<span className="error-message">{errors.message}</span>
)}
{formData.message.length > 0 && (
<small className="char-count">
{formData.message.length}/500文字
</small>
)}
</div>
<button
type="submit"
disabled={isSubmitting}
className={isSubmitting ? 'submitting' : ''}
>
{isSubmitting ? '送信中...' : '送信する'}
</button>
</form>
);
}
パフォーマンスを考慮したベストプラクティス
条件レンダーのベストプラクティス
- 早期リターンの活用: 複雑な条件分岐では、該当しない条件で早期にリターンすることで、ネストを浅く保つ
- 適切な手法の選択:
- 大きく異なる構造 →
if文
- 小さな違いや切り替え →
三項演算子
- 表示・非表示の切り替え →
&&演算子
- 複雑な条件ロジック →
変数代入
- 大きく異なる構造 →
- 可読性の優先: 短いコードよりも理解しやすいコードを心がける
- 条件の明示化: 数値をそのまま
&&
の左辺に置かず、明示的に真偽値で評価する
3. アクセシビリティを考慮した条件レンダー
function AccessibleModal({ isOpen, onClose, title, children }) {
// モーダルが開いていない場合は何も表示しない
if (!isOpen) {
return null;
}
return (
<div
className="modal-overlay"
onClick={onClose}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div
className="modal-content"
onClick={(e) => e.stopPropagation()}
>
<div className="modal-header">
{title && (
<h2 id="modal-title">{title}</h2>
)}
<button
className="close-button"
onClick={onClose}
aria-label="モーダルを閉じる"
>
✕
</button>
</div>
<div className="modal-body">
{children}
</div>
</div>
</div>
);
}
まとめ
React の条件レンダーは、動的で柔軟なユーザーインターフェースを構築するための重要な技術です。本記事で解説した手法を適切に使い分けることで、保守性が高く理解しやすいコードを書くことができます。
重要ポイントの復習
- if文: 明確で理解しやすく、複雑な条件分岐に適している
- null返却: 要素を完全に非表示にしたい場合に使用
- 三項演算子: 簡潔で、小さな差異の切り替えに便利
- 論理AND演算子: 表示・非表示の制御に最適
- 変数代入: 複雑な条件ロジックの整理に効果的
条件レンダーをマスターすることで、ユーザーエクスペリエンスを大幅に向上させることができます。状況に応じて最適な手法を選択し、常にコードの可読性とメンテナンス性を意識して実装していきましょう。
実際の開発では、これらの手法を組み合わせながら使用することが多くなります。まずは基本的なパターンから始めて、徐々に複雑な条件レンダーにチャレンジしてみてください。