条件渲染

根据不同的条件显示不同的 UI

什么是条件渲染?

条件渲染允许你根据应用程序的状态、用户输入或其他条件, 动态地显示或隐藏组件的某些部分。

在 React 中,你可以使用 JavaScript 的条件语句(如 if&&、三元运算符)来实现条件渲染。

核心思想

React 中的条件渲染就是使用 JavaScript 的条件逻辑来决定渲染什么。 与其他框架中的特殊语法不同,React 使用的是普通的 JavaScript。

使用 if 语句

在渲染前使用 if

最直接的方式是在 return 之前使用 if 语句:

TypeScript
function Greeting({ isLoggedIn }) {
  let content;

  if (isLoggedIn) {
    content = <h1>欢迎回来!</h1>;
  } else {
    content = <h1>请先登录</h1>;
  }

  return <div>{content}</div>;
}

提前返回

你可以使用提前返回来简化代码:

TypeScript
function Greeting({ isLoggedIn }) {
  if (!isLoggedIn) {
    return <LoginPrompt />;
  }

  return <WelcomeMessage />;
}

返回 null

当你不想渲染任何内容时,可以返回 null:

TypeScript
function WarningBanner({ warn }) {
  if (!warn) {
    return null;
  }

  return (
    <div className="warning">
      警告!
    </div>
  );
}
最佳实践

提前返回通常比嵌套的 if-else 更清晰。 优先考虑使用提前返回或提前返回 null 的方式。

三元运算符

基本用法

三元运算符 condition ? trueValue : falseValue 是在 JSX 中进行条件渲染的常用方式:

TypeScript
function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <LogoutButton />
      ) : (
        <LoginButton />
      )}
    </div>
  );
}

内联条件

你可以在 JSX 中内联使用三元运算符:

TypeScript
function UserCard({ user }) {
  return (
    <div className="card">
      <h2>{user.name}</h2>
      <p>
        状态: {user.isActive ? (
          <span className="active">活跃</span>
        ) : (
          <span className="inactive">未激活</span>
        )}
      </p>
    </div>
  );
}

三元运算符 vs if

TypeScript
// ✅ 三元运算符:简单的二选一
{isLoggedIn ? <Dashboard /> : <Login />}

// ❌ 不要用于复杂条件
{condition1 ? (
  condition2 ? <A /> : <B />
) : (
  condition3 ? <C /> : <D />
)}

// ✅ 复杂条件使用函数
function renderContent() {
  if (condition1) {
    return condition2 ? <A /> : <B />;
  }
  return condition3 ? <C /> : <D />;
}

{renderContent()}
注意

虽然三元运算符很方便,但嵌套的三元运算符会使代码难以阅读。 当条件复杂时,考虑使用函数或提前返回。

逻辑与运算符 (&&)

基本用法

使用 && 运算符,当条件为 true 时才渲染元素:

TypeScript
function Cart({ items }) {
  return (
    <div>
      <h1>购物车</h1>
      {items.length > 0 && (
        <p>你有 {items.length} 件商品</p>
      )}
    </div>
  );
}

工作原理

在 JavaScript 中,true && expression 返回 expression, 而 false && expression 返回 false:

TypeScript
true && <div>Hello</div>  // 渲染 <div>Hello</div>
false && <div>Hello</div> // 不渲染任何内容
"hello" && <div>Hi</div> // 渲染 <div>Hi</div>
"" && <div>Hi</div>       // 不渲染任何内容

常见用例

TypeScript
function TaskList({ tasks }) {
  return (
    <div>
      <h2>任务列表</h2>

      {/* 显示未完成任务数 */}
      {tasks.filter(t => !t.completed).length > 0 && (
        <p className="info">
          你有 {tasks.filter(t => !t.completed).length} 个未完成任务
        </p>
      )}

      {/* 渲染任务列表 */}
      <ul>
        {tasks.map(task => (
          <li key={task.id}>
            {task.text}
          </li>
        ))}
      </ul>
    </div>
  );
}
常见陷阱

不要在 && 的左侧使用数字:

TypeScript
// ❌ 错误:0 会被渲染
{items.length && <ItemList items={items} />}

// ✅ 正确:明确比较
{items.length > 0 && <ItemList items={items} />}

逻辑或运算符 (||)

提供默认值

使用 || 运算符提供默认值:

TypeScript
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.name || '匿名用户'}</h2>
      <p>{user.bio || '这个人很懒,什么都没写'}</p>
    </div>
  );
}

Nullish Coalescing (??)

使用 ?? 运算符只在值为 nullundefined 时使用默认值:

TypeScript
function Display({ count, name }) {
  return (
    <div>
      {/* || 会认为 0 是 falsy */}
      <p>数量: {count || '未知'}</p> {/* 0 会显示为 "未知" */}

      {/* ?? 只在 null/undefined 时使用默认值 */}
      <p>数量: {count ?? '未知'}</p> {/* 0 会正常显示 */}

      {/* 空字符串 */}
      <p>名称: {name || '未命名'}</p> {/* "" 会显示为 "未命名" */}
      <p>名称: {name ?? '未命名'}</p> {/* "" 会正常显示 */}
    </div>
  );
}
选择合适的运算符
  • &&:条件为真时渲染
  • ||:falsy 值时使用默认值
  • ??:null/undefined 时使用默认值

组件级别的条件渲染

条件性渲染组件

TypeScript
function Page({ user, isLoading }) {
  if (isLoading) {
    return <LoadingSpinner />;
  }

  if (!user) {
    return <LoginForm />;
  }

  return <Dashboard user={user} />;
}

包装组件

TypeScript
function ConditionalWrapper({ condition, wrapper, children }) {
  return condition ? wrapper(children) : children;
}

// 使用
<ConditionalWrapper
  condition={isLink}
  wrapper={children => <a href="/">{children}</a>}
>
  <button>点击</button>
</ConditionalWrapper>

高阶组件 (HOC)

TypeScript
function withAuth(WrappedComponent) {
  return function AuthComponent(props) {
    const { isAuthenticated, ...rest } = props;

    if (!isAuthenticated) {
      return <LoginPrompt />;
    }

    return <WrappedComponent {...rest} />;
  };
}

// 使用
const ProtectedPage = withAuth(Dashboard);

<ProtectedPage isAuthenticated={true} />

隐藏 vs 不渲染

CSS 隐藏

TypeScript
function Panel({ isVisible, children }) {
  return (
    <div style={{ display: isVisible ? 'block' : 'none' }}>
      {children}
    </div>
  );
}

// 元素仍然在 DOM 中,只是不可见
<Panel isVisible={false}>
  <input type="text" />
</Panel>

条件渲染

TypeScript
function Panel({ isVisible, children }) {
  if (!isVisible) {
    return null;
  }

  return <div>{children}</div>;
}

// 元素不在 DOM 中
<Panel isVisible={false}>
  <input type="text" />
</Panel>

如何选择?

方法DOM 存在保留状态性能适用场景
CSS 隐藏频繁切换,保留状态
条件渲染更快完全不需要时
性能考虑

如果组件包含昂贵的初始化或大量数据,使用 CSS 隐藏可以避免重新创建组件。 但要注意隐藏的组件仍在内存中。

常见模式

加载状态

TypeScript
function DataList({ data, isLoading, error }) {
  if (isLoading) {
    return <Spinner />;
  }

  if (error) {
    return <ErrorMessage error={error} />;
  }

  if (!data || data.length === 0) {
    return <EmptyState />;
  }

  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

权限控制

TypeScript
function AdminPanel({ user }) {
  if (!user) {
    return <LoginPrompt />;
  }

  if (!user.isAdmin) {
    return <AccessDenied />;
  }

  return <AdminDashboard />;
}

多条件渲染

TypeScript
function getStatusBadge(status) {
  switch (status) {
    case 'success':
      return <Badge variant="success">成功</Badge>;
    case 'warning':
      return <Badge variant="warning">警告</Badge>;
    case 'error':
      return <Badge variant="error">错误</Badge>;
    default:
      return <Badge variant="info">未知</Badge>;
  }
}

function Task({ task }) {
  return (
    <div>
      <span>{task.name}</span>
      {getStatusBadge(task.status)}
    </div>
  );
}

枚举模式

TypeScript
const StateComponents = {
  loading: LoadingSpinner,
  error: ErrorDisplay,
  success: SuccessMessage,
  empty: EmptyState,
};

function DataDisplay({ state, data }) {
  const Component = StateComponents[state];
  return <Component data={data} />;
}
提示

对于多种状态的渲染,使用对象映射比多个 if-else 或三元运算符更清晰。 这种模式称为"枚举模式"。

最佳实践

1. 保持简单

TypeScript
// ✅ 好:简单明了
{isLoggedIn && <Dashboard />}

// ❌ 差:不必要的复杂
{isLoggedIn === true ? <Dashboard /> : null}

2. 提取复杂逻辑

TypeScript
// ❌ 差:JSX 中有复杂逻辑
function UserList({ users }) {
  return (
    <div>
      {users.filter(u => u.isActive).map(u => (
        <div key={u.id}>
          {u.name} {u.role === 'admin' && '(管理员)'}
        </div>
      ))}
    </div>
  );
}

// ✅ 好:提取逻辑
function UserList({ users }) {
  const activeUsers = users.filter(u => u.isActive);

  return (
    <div>
      {activeUsers.map(user => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

3. 使用明确的条件

TypeScript
// ❌ 差:隐式转换
{items.length && <List />}

// ✅ 好:明确比较
{items.length > 0 && <List />}

4. 避免深层嵌套

TypeScript
// ❌ 差:深层嵌套
{condition1 && (
  condition2 ? (
    condition3 ? <A /> : <B />
  ) : (
    <C />
  )
)}

// ✅ 好:使用提前返回
function renderContent() {
  if (!condition1) return <C />;
  if (!condition2) return <C />;
  if (condition3) return <A />;
  return <B />;
}

5. 使用 TypeScript

TypeScript
interface Props {
  status: 'loading' | 'success' | 'error';
  data?: DataType;
  error?: Error;
}

function DataDisplay({ status, data, error }: Props) {
  switch (status) {
    case 'loading':
      return <Spinner />;
    case 'success':
      return <DataView data={data!} />;
    case 'error':
      return <ErrorView error={error!} />;
  }
}

常见问题

条件不工作?

确保你在比较值时使用正确的运算符:

TypeScript
// ❌ 错误:赋值而不是比较
if (value = 'hello') { }

// ✅ 正确:比较
if (value === 'hello') { }

数字 0 不显示?

TypeScript
// ❌ 错误:0 是 falsy
{count && <span>{count}</span>}

// ✅ 正确:明确比较
{count > 0 && <span>{count}</span>}

如何处理多个条件?

TypeScript
// 使用函数处理复杂逻辑
function shouldShowComponent(props) {
  return props.isEnabled &&
         props.isVisible &&
         props.hasPermission;
}

{shouldShowComponent(props) && <Component />}

三元运算符嵌套太深?

TypeScript
// 将复杂的条件逻辑提取为函数
function renderContent(props) {
  if (props.isLoading) return <Loading />;
  if (props.error) return <Error />;
  if (!props.data) return <Empty />;
  return <Content data={props.data} />;
}

{renderContent(props)}

相关概念

这篇文章有帮助吗?