首页 / 文章 / 使用 React 条件渲染处理现实世界中的 UI 状态

使用 React 条件渲染处理现实世界中的 UI 状态

学习如何运用实用的条件渲染模式,在 React 中构建身份验证、角色管理、权限控制以及加载状态、错误提示和空状态等 UI。

2186 词

在上一篇文章中,您已经了解了条件渲染的基础知识——通过简单的条件来告诉 React 应该显示哪个组件、另一个组件,还是什么都不显示。

然而,在实际应用中情况很少如此简单:

isLoggedIn ? <Dashboard /> : <Login />

以一个真实的控制面板为例。用户可能处于以下任意一种状态:

  • 已登出
  • 正在登录中
  • 已登录
  • 管理员身份
  • 普通用户
  • 缺少必要权限
  • 正在等待数据返回
  • 遇到 API 错误
  • 正在查看空结果集

每种情况都需要不同的用户界面。正是在这里,条件渲染不再只是一个小技巧,而变成了构建应用程序的真正工具。

让我们看看这在专业级的 React 代码中是如何实现的。

1. 基于身份验证的渲染

典型的应用场景就是身份验证。想象一个拥有两种可能界面的应用程序:

Not Logged In
      ↓
Login Page

Logged In
      ↓
Dashboard

React可以轻松地在它们之间切换:

function App() {
  const isLoggedIn = true;
return (
    <>
      {isLoggedIn ? <Dashboard /> : <Login />}
    </>
  );
}

不过,实际应用通常还需要第三种状态:加载中,因为应用程序可能需要一些时间来确认用户的令牌是否有效。此时的流程如下:

Checking Authentication
        ↓
      Loading
        ↓
  Authenticated?
   ↙          ↘
 YES          NO
 ↓             ↓
Dashboard     Login

在代码中,这可能表现为:

function App() {
  const isLoading = false;
  const isLoggedIn = true;
if (isLoading) {
    return <LoadingSpinner />;
  }
  return isLoggedIn
    ? <Dashboard />
    : <Login />;
}

你会在无数实际运行的React应用中看到这种模式反复出现。

2. 基于角色的渲染

身份验证能告诉你谁已登录,而授权则能告诉你该用户被允许执行哪些操作。

以员工管理工具为例,管理员可能会看到:

View Employees
Add Employee
Edit Employee
Delete Employee

而普通用户只能看到:

View Employees

可以根据用户的角色有条件地显示相应操作:

function EmployeeCard({ userRole }) {
  return (
    <div>
      <h2>Employee Details</h2>
      <button>View</button>
      {userRole === "admin" && (
        <>
          <button>Edit</button>
          <button>Delete</button>
        </>
      )}
    </div>
  );
}

通过这种设置,仅管理员才能看到的控件只会显示给管理员。

重要的安全提示

条件渲染可以控制用户界面中显示的内容,但隐藏元素并不能替代真正的安全措施。例如:

{isAdmin && <DeleteButton />}

这样普通用户就看不到该按钮,但服务器仍需独立确认请求确实来自有权限删除数据的人。可以这样理解这种分工:

Frontend
↓
Controls what users SEE

Backend
↓
Controls what users CAN DO

切勿将客户端条件渲染作为授权机制。

3. 基于权限的用户界面

规模较大的应用程序往往需要比简单角色更细粒度的权限控制:

Admin
User

相反,你可以定义具体的权限,例如:

CAN_VIEW_USERS
CAN_EDIT_USERS
CAN_DELETE_USERS
CAN_EXPORT_REPORT

这样,组件就可以根据现有的权限分别渲染每个操作:

function UserActions({ permissions }) {
  return (
    <>
      {permissions.includes("CAN_EDIT_USERS") && (
        <button>Edit</button>
      )}
      {permissions.includes("CAN_DELETE_USERS") && (
        <button>Delete</button>
      )}
    </>
  );
}

这种方法能让您更精确地控制每个用户能够执行哪些操作。

4. 加载状态

假设控制面板发起了一个需要两秒才能响应的API调用,在这段时间内屏幕上应该显示什么?当然不是空白页面——而应该是加载指示器:

if (loading) {
  return <p>Loading products...</p>;
}

整体流程如下:

API Request
    ↓
Loading = true
    ↓
Show Loader
    ↓
API Response
    ↓
Loading = false
    ↓
Show Content

即使网络速度较慢,加载指示器也能让应用程序显得响应迅速。

5. 骨架加载器

与其显示纯文本消息,不如:

Loading...

许多现代界面会显示与即将加载的内容形状相似的占位符——即骨架加载器。例如:

┌──────────────────────┐
│ █████████████        │
│ ███████              │
│ █████████████████    │
└──────────────────────┘

当真实数据返回后,它会替换占位符:

┌──────────────────────┐
│ MacBook Air          │
│ ₹99,999              │
│ ⭐⭐⭐⭐⭐             │
└──────────────────────┘

底层的 React 逻辑依然保持简单:

return loading
  ? <ProductSkeleton />
  : <ProductCard />;

这里唯一的真正区别在于它为用户体验带来的优化。

6. 错误状态

API 调用并不总是一帆风顺。

连接可能会中断。

服务器可能会崩溃。

请求也可能会出现超时情况。

与其让应用崩溃或卡住,不如渲染错误状态。

if (error) {
  return (
    <div>
      <h2>Something went wrong.</h2>
      <button>Try Again</button>
    </div>
  );
}

优雅地处理故障是高质量用户界面的重要特征。

7. 加载中 + 错误 + 成功

在实际使用中,这三种状态几乎总是同时出现。

function ProductList({
  loading,
  error,
  products
}) {
      if (loading) {
    return <p>Loading...</p>;
      }
  if (error) {
    return <p>Something went wrong.</p>;
      }
  return <Products products={products} />;
     }

你可以这样理解其流程:

Request
   │
   ├── Loading → Loader
   │
   ├── Failed → Error
   │
   └── Success → Data

在后续学习 API 调用和 useEffect 时,你会反复看到这种相同的模式。

8. 空状态

请求成功并不代表就有实际数据可以显示。

假设用户搜索了类似的内容:

"React Quantum Pizza Developer"

API 调用本身顺利完成,没有出现任何错误。

但返回的结果可能如下所示:

products.length === 0

不要让屏幕保持空白,而应给用户展示有意义的内容。

if (products.length === 0) {
  return (
    <div>
      <h2>No Products Found</h2>
      <p>Try changing your search.</p>
    </div>
  );
}

空状态对打造流畅的用户体验至关重要。

加载中、空状态与错误状态

新入门的 React 开发者常常混淆这三种状态,但实际上它们代表完全不同的情况。

LOADING
Data hasn't arrived yet.

EMPTY
Data arrived, but nothing exists.

ERROR
Something failed.

一个完善的程序会分别处理这三种状态。

9. 多重条件

有时,渲染结果取决于多个条件共同作用的结果。

请看以下流程:

Is User Logged In?
        ↓
Is Subscription Active?
        ↓
Is User Admin?
        ↓
Show Admin Dashboard

人们很容易想把所有这些内容都塞进一个巨大的嵌套三元运算式中:

condition1
  ? condition2
    ? condition3
      ? <A />
      : <B />
    : <C />
  : <D />

这样的代码可以正常编译。

但阅读起来却如同噩梦一般。

更好的方法是把每个条件单独清晰地呈现出来。

if (!isLoggedIn) {
  return <Login />;
}

if (!hasSubscription) {
  return <UpgradePlan />;
}

if (isAdmin) {
  return <AdminDashboard />;
}

return <UserDashboard />;

这种版本要容易理解得多。

10. 保护性语句

你刚刚看到的这种模式有一个名称:保护性语句,也被称为提前返回。

不必在条件中再嵌套条件:

if
 └── if
      └── if
           └── UI

应提前处理边缘情况并立即返回。

if (loading) return <Loader />;
if (error) return <ErrorPage />;
if (!user) return <Login />;
return <Dashboard />;

这样代码结构清晰。

易于阅读。

而且调试起来也方便得多。

像 React 开发者一样思考

在编写组件之前,先问自己以下几个问题会有所帮助:

“这个界面可能处于哪些状态?”

对于通过 API 调用驱动的页面,状态列表可能如下所示:

Loading
Error
Empty
Success

在认证流程中,状态列表可能为:

Logged Out
Checking Authentication
Logged In
Unauthorized

在开始编码之前先规划好这些状态,能让后续开发的组件更易于理解。

初学者常见错误

过度使用嵌套条件语句

不要为了节省几行代码而牺牲可读性。

忽略空状态

API 返回空数组并不等同于错误——应将其视为独立的情况处理。

混淆界面隐藏与真正安全措施

仅仅通过隐藏某些内容:

<DeleteButton />

无法阻止他人直接访问你的 API 接口。

真正的授权机制必须存在于后端。

&& 渲染错误的内容

需注意如下代码:

{items.length && <ProductList />}

如果 items.length 的值为 0,React 可能会渲染出:

0

直接显示在页面上。

更安全的写法是:

{items.length > 0 && <ProductList />}

这样条件表达式就会返回真正的布尔值。

最佳实践

在条件渲染时,可读性应始终放在首位。

建议采用如下模式:

if (loading) return <Loader />;

而非在深度嵌套的 JSX 中堆砌大量条件。

对于经常需要渲染的状态,应将其提取为独立的可复用组件:

<Loader />
<ErrorMessage />
<EmptyState />

随着组件变得愈发复杂,应将业务逻辑与实际显示的内容分开。

不要只考虑正常情况——要为界面可能出现的所有状态做好规划。

小型项目:智能控制面板

作为练习,试着构建一个能够处理以下情况的控制面板:

User Not Logged In
        ↓
Login ScreenUser

    Logged In
        ↓
Loading Dashboard
        ↓
 ┌──────┴──────┐
Error          Success
 ↓                ↓
Error UI       Data Exists?
               ↙       ↘
             YES        NO
              ↓          ↓
          Dashboard   Empty State

然后在之上添加基于角色的功能:

Admin
↓
Edit + Delete

User
↓
View Only

这样的项目会同时运用多个概念:

  • 属性
  • 状态
  • 事件
  • 条件渲染

这正是当你开始构建实际应用时,React的各个组件开始协同工作的方式。

面试问题

什么是条件渲染?

它是指根据应用程序的当前状态或条件来显示不同用户界面的做法。

&&与三元运算符有什么区别?

当只需在条件为真时显示内容且其他情况下不显示任何东西时,使用 &&。若需要在条件为真和为假时分别显示不同内容,则应使用三元运算符。

什么算作空状态?

指请求成功但没有任何数据可显示时所呈现的界面。

前端基于角色的渲染足以保障安全吗?

不够。这只是界面上的便利功能——实际的授权检查仍需在后台进行。

提前返回的好处是什么?

它能减少嵌套结构,从而使条件组件更易于阅读和维护。

核心要点

至此,您已经掌握了React中的条件渲染的全部内容。

你已经看到实际应用是如何处理登录状态与未登录状态的界面展示、按角色限制屏幕访问、通过细粒度的权限控制功能启用/禁用、在数据加载时显示加载指示器、使用骨架占位符代替纯文本、在请求失败时展示错误信息、在结果集为空时向用户发送提示、将多个条件整合为连贯的流程、用保护语句简化嵌套检查,以及运用那些能让实际代码库中的逻辑更易于维护的通用做法。

条件渲染能够让你的应用根据当前所处的情况呈现最合适的体验

但仍然存在一个挑战。

假设某个 API 返回:

1,000 Products

你真的会逐一写出:

<Product />
<Product />
<Product />
...

一千次吗?

显然不会。

React 有更出色的方式来处理这个问题。

第9A部分中,你将学习如何使用 map() 渲染列表,并了解单个组件如何直接从数据中生成数百甚至数千个 UI 元素。

紧接着,你将面对 React 最经典的面试问题之一:

为什么 React 需要 key

我们在第9A部分——React 中的列表渲染与键值中见。

相关阅读

  • 为 React 建立思维模型:状态同步、状态与钩子 — 了解 React 核心概念——状态同步、组件、属性、状态及钩子——背后的原理,从而培养直觉而非死记硬背 API。
  • React 组件入门:构建可复用、易维护的 UI 组件 — 了解为何将 UI 拆分为小型 React 组件能提升可复用性、可读性及团队协作效率,随后动手创建你的第一个功能组件。
  • 使用 Service Workers 为 Web 应用启用离线支持 — 了解如何利用 Service Workers 和 Cache API 让网站即时加载,并在无网络连接时仍能正常运行。
  • 现代 JavaScript 的真正复杂性源自工具而非语言本身 — 本文阐述了 async/await 和可选链等核心 JavaScript 特性如何简化代码,而过多的工具和依赖却会带来不必要的复杂性。
  • 利用AI进行React开发:真正的优势与局限 — 阐述了AI编程助手在哪些方面真正能提升React开发效率,又在哪些方面存在不足,同时还提供了在不影响代码质量的前提下使用这些工具的实际工作流程。