React 條件渲染 (Conditional Rendering)

在 React 中,你可以根據不同的條件來渲染不同的 UI。這就是所謂的條件渲染,有幾種常見的寫法可以達成這個目的。

JavaScript if else

最直接的方式就是使用 JavaScript 的 if else 語法:

function Greeting({ isLoggedIn }) {
  if (isLoggedIn) {
    return <h1>歡迎回來!</h1>;
  }
  return <h1>請先登入</h1>;
}

// 使用
<Greeting isLoggedIn={true} />   // 顯示「歡迎回來!」
<Greeting isLoggedIn={false} />  // 顯示「請先登入」

這種方式適合在需要根據條件回傳完全不同的 JSX 結構時使用。

三元運算子 ? :

三元運算子(Conditional Ternary Operator)是最常用的條件渲染方式,因為它可以直接寫在 JSX 中:

function Greeting({ isLoggedIn }) {
  return <h1>{isLoggedIn ? '歡迎回來!' : '請先登入'}</h1>
}

語法:

{
  條件 ? 條件為真時的內容 : 條件為假時的內容
}

三元運算子也可以用來渲染不同的元件:

function UserPanel({ isLoggedIn }) {
  return <div>{isLoggedIn ? <UserDashboard /> : <LoginForm />}</div>
}

邏輯 AND 運算子 &&

當你只想在條件為真時渲染某些內容,條件為假時不渲染任何東西,可以使用 && 邏輯運算子:

function Notification({ hasMessages, messageCount }) {
  return (
    <div>
      <h1>通知中心</h1>
      {hasMessages && <p>你有 {messageCount} 則新訊息</p>}
    </div>
  )
}

語法:

{
  條件 && 要渲染的內容
}

如果條件為 true,會渲染 && 後面的內容;如果條件為 false,則不渲染任何東西。

注意 && 的陷阱

使用 && 時要小心數字 0 的情況:

// ⚠️ 當 count 為 0 時,會渲染 "0" 而不是不渲染
{
  count && <p>Count: {count}</p>
}

// ✅ 正確的寫法
{
  count > 0 && <p>Count: {count}</p>
}

// ✅ 或者轉換成布林值
{
  !!count && <p>Count: {count}</p>
}

邏輯 OR 運算子 ||

|| 運算子可以用來提供預設值:

function UserName({ name }) {
  return <h1>Hello, {name || '訪客'}</h1>
}

// 如果 name 是空字串或 undefined,會顯示「Hello, 訪客」

Nullish Coalescing 運算子 ??

?? 運算子只在值為 nullundefined 時才使用預設值(不像 || 會對所有 falsy 值生效):

function Count({ count }) {
  // 如果 count 是 0,會正確顯示 0
  // 只有 count 是 null 或 undefined 時才顯示預設值
  return <p>數量:{count ?? '未設定'}</p>
}

將元素存在變數中

當條件邏輯比較複雜時,可以先將元素存在變數中:

function LoginControl() {
  const [isLoggedIn, setIsLoggedIn] = useState(false)

  // 根據狀態決定要顯示的按鈕
  let button
  if (isLoggedIn) {
    button = <button onClick={() => setIsLoggedIn(false)}>登出</button>
  } else {
    button = <button onClick={() => setIsLoggedIn(true)}>登入</button>
  }

  return (
    <div>
      <h1>{isLoggedIn ? '歡迎!' : '請登入'}</h1>
      {button}
    </div>
  )
}

不渲染任何內容

有時候你希望元件在某些條件下不要渲染任何東西。這時可以 return null

function WarningBanner({ show, message }) {
  // 不顯示警告時,直接返回 null
  if (!show) {
    return null
  }

  return <div className="warning">⚠️ {message}</div>
}

// 使用
function App() {
  const [showWarning, setShowWarning] = useState(true)

  return (
    <div>
      <WarningBanner show={showWarning} message="這是警告訊息" />
      <button onClick={() => setShowWarning(!showWarning)}>
        {showWarning ? '隱藏' : '顯示'}警告
      </button>
    </div>
  )
}
從元件返回 null 不會影響元件的生命週期。

多重條件

當有多個條件需要判斷時,可以使用多個 if else 或串連的三元運算子:

使用 if else

function StatusIcon({ status }) {
  if (status === 'success') {
    return <span>✅</span>
  } else if (status === 'error') {
    return <span>❌</span>
  } else if (status === 'loading') {
    return <span>⏳</span>
  }
  return <span>❓</span>
}

使用物件映射

當條件很多時,使用物件映射會更清晰:

function StatusIcon({ status }) {
  const icons = {
    success: '✅',
    error: '❌',
    loading: '⏳',
    default: '❓',
  }

  return <span>{icons[status] || icons.default}</span>
}

使用 switch

function StatusMessage({ status }) {
  switch (status) {
    case 'loading':
      return <p>載入中...</p>
    case 'success':
      return <p>操作成功!</p>
    case 'error':
      return <p>發生錯誤</p>
    default:
      return null
  }
}

實際範例:登入狀態管理

import { useState } from 'react'

function App() {
  const [user, setUser] = useState(null)

  function handleLogin() {
    // 模擬登入
    setUser({ name: 'Mike', email: 'mike@example.com' })
  }

  function handleLogout() {
    setUser(null)
  }

  return (
    <div>
      <header>
        {user ? (
          <div>
            <span>歡迎,{user.name}!</span>
            <button onClick={handleLogout}>登出</button>
          </div>
        ) : (
          <button onClick={handleLogin}>登入</button>
        )}
      </header>

      <main>{user ? <Dashboard user={user} /> : <WelcomePage />}</main>
    </div>
  )
}

function Dashboard({ user }) {
  return (
    <div>
      <h1>個人儀表板</h1>
      <p>Email: {user.email}</p>
    </div>
  )
}

function WelcomePage() {
  return (
    <div>
      <h1>歡迎來到我們的網站</h1>
      <p>請登入以查看更多內容</p>
    </div>
  )
}