(七)React 条件渲染原理分析
一、什么是条件渲染?—— 让组件"看情况"显示内容
在开发中,很多场景需要组件根据不同条件显示不同内容:
- 用户登录后显示"个人中心"按钮,未登录时显示"登录"按钮;
- 数据加载时显示"加载中…",加载完成后显示数据列表;
- 表单验证不通过时显示错误提示,通过后隐藏提示。
这种"根据条件决定渲染内容"的机制,就是条件渲染。它本质上是通过JavaScript的条件判断语句(如if-else
、三元表达式),控制组件返回的JSX结构。
二、条件渲染的3种基础实现方式
React没有专门的条件渲染语法,而是直接使用JavaScript的条件判断。下面介绍最常用的3种方式,根据场景选择合适的方法。
1. if-else判断:适合逻辑较复杂的场景
当条件判断逻辑较多(比如多个分支)时,用if-else
最清晰。在组件中先通过if-else
确定要渲染的内容,再返回结果。
示例1:登录状态切换
import { useState } from 'react';function LoginStatus() {// 用state存储登录状态(true为已登录,false为未登录)const [isLoggedIn, setIsLoggedIn] = useState(false);// 点击按钮切换登录状态const toggleLogin = () => {setIsLoggedIn(!isLoggedIn);};// 方式1:用if-else判断,定义要渲染的内容if (isLoggedIn) {return (<div><p>欢迎回来!</p><button onClick={toggleLogin}>注销</button></div>);} else {return (<div><p>请先登录</p><button onClick={toggleLogin}>登录</button></div>);}
}
示例2:多条件判断(用户角色区分)
function UserRole({ role }) {// 根据role值显示不同内容(admin/editor/visitor)if (role === 'admin') {return <p>您是管理员,拥有全部权限</p>;} else if (role === 'editor') {return <p>您是编辑,可修改内容</p>;} else {return <p>您是访客,只能查看内容</p>;}
}// 使用时传递不同role
function App() {return (<div><UserRole role="admin" /><UserRole role="editor" /><UserRole role="visitor" /></div>);
}
优点:逻辑清晰,适合多分支判断;
缺点:代码较长,不适合简单的二选一场景。
2. 三元表达式:适合简单的二选一场景
当只有两种情况需要判断时,用三元表达式(condition ? a : b
)更简洁,可以直接嵌入JSX中。
示例1:开关状态显示
import { useState } from 'react';function ToggleSwitch() {const [isOn, setIsOn] = useState(false);return (<div><button onClick={() => setIsOn(!isOn)}>{isOn ? '关闭' : '开启'} {/* 三元表达式:条件为真显示'关闭',否则显示'开启' */}</button><p>当前状态:{isOn ? '已开启' : '已关闭'}</p></div>);
}
示例2:结合样式的条件渲染
import { useState } from 'react';function Notification() {const [hasError, setHasError] = useState(false);return (<div><button onClick={() => setHasError(true)}>触发错误</button>{/* 三元表达式控制样式和内容 */}<div style={{padding: '10px',margin: '10px 0',backgroundColor: hasError ? 'red' : 'green',color: 'white'}}>{hasError ? '操作失败!' : '操作成功!'}</div></div>);
}
优点:简洁,可直接嵌入JSX,适合二选一场景;
缺点:多层嵌套时可读性差(不建议嵌套超过两层)。
3. 逻辑与(&&)运算:适合"条件为真才显示"的场景
如果只需要在条件为true
时显示内容,条件为false
时不显示(或显示空),可以用逻辑与(&&
)运算。
原理:
JavaScript中,A && B
的规则是:
- 如果A为
true
,则结果为B; - 如果A为
false
,则结果为A(即false
,React会忽略false
,不渲染内容)。
示例1:消息提示(有新消息才显示)
import { useState } from 'react';function MessageAlert() {const [unreadCount, setUnreadCount] = useState(0);return (<div><button onClick={() => setUnreadCount(3)}>收到新消息</button>{/* 当unreadCount > 0时,显示消息提示 */}{unreadCount > 0 && (<p style={{ color: 'blue' }}>您有{unreadCount}条未读消息</p>)}</div>);
}
示例2:列表为空时显示提示
import { useState } from 'react';function TodoList() {const [todos, setTodos] = useState([]); // 初始为空列表return (<div><button onClick={() => setTodos([{ id: 1, text: '学习条件渲染' }])}>添加待办</button><ul>{/* 列表有内容时渲染列表项 */}{todos.map(todo => (<li key={todo.id}>{todo.text}</li>))}</ul>{/* 列表为空时显示提示 */}{todos.length === 0 && <p>暂无待办事项,点击按钮添加</p>}</div>);
}
优点:比三元表达式更简洁,适合"条件为真才显示"的场景;
注意:避免条件是"数字0"的情况(比如count && <p>{count}</p>
,当count为0时会显示0,因为0在JS中是 falsy 值,但React会渲染0)。
三、条件渲染的进阶技巧
1. 用变量存储条件结果:简化复杂渲染逻辑
当条件渲染的内容较长时,直接在JSX中写判断会导致代码混乱。可以先将渲染内容赋值给变量,再在JSX中使用变量。
import { useState } from 'react';function ComplexCondition() {const [status, setStatus] = useState('loading'); // loading/success/error// 1. 定义变量存储要渲染的内容let content;if (status === 'loading') {content = <p>加载中...</p>;} else if (status === 'success') {content = <p>数据加载成功!</p>;} else {content = <p style={{ color: 'red' }}>加载失败,请重试</p>;}// 2. 在JSX中使用变量return (<div><button onClick={() => setStatus('success')}>模拟成功</button><button onClick={() => setStatus('error')}>模拟失败</button>{content} {/* 直接渲染变量 */}</div>);
}
这种方式让JSX结构更清晰,尤其适合多条件判断的场景。
2. 组件级条件渲染:拆分条件为独立组件
如果条件逻辑复杂,可以将不同条件对应的内容拆分成独立组件,再通过父组件的条件决定渲染哪个子组件。
// 1. 拆分不同状态的组件
function Loading() {return <p>加载中...</p>;
}function Success() {return <p>数据加载成功!</p>;
}function Error() {return <p style={{ color: 'red' }}>加载失败,请重试</p>;
}// 2. 父组件根据条件渲染子组件
import { useState } from 'react';function DataLoader() {const [status, setStatus] = useState('loading');return (<div><button onClick={() => setStatus('success')}>模拟成功</button><button onClick={() => setStatus('error')}>模拟失败</button>{/* 根据status渲染不同组件 */}{status === 'loading' && <Loading />}{status === 'success' && <Success />}{status === 'error' && <Error />}</div>);
}
优点:组件职责单一,代码复用性高,适合大型项目。
3. 返回null:让组件"什么都不显示"
有时需要组件在特定条件下完全不显示任何内容,这时可以让组件返回null
。
import { useState } from 'react';function SecretMessage() {const [isAuthorized, setIsAuthorized] = useState(false);// 如果未授权,返回null(不显示任何内容)if (!isAuthorized) {return null;}// 授权后显示内容return <p>这是秘密消息,只有授权用户能看到</p>;
}function App() {return (<div><h3>消息区域:</h3><SecretMessage /><button onClick={() => setIsAuthorized(true)}>获取授权</button></div>);
}
点击"获取授权"前,SecretMessage
组件返回null
,页面上不显示任何内容;点击后才显示秘密消息。
四、条件渲染的常见陷阱与避坑指南
1. 避免不必要的条件判断
不要过度使用条件渲染,简单的动态内容可以直接通过变量嵌入。比如:
// 不好的写法:没必要的条件判断
{isLoggedIn ? <p>欢迎{username}</p> : null}// 更好的写法:直接嵌入变量(未登录时username为空,显示"欢迎")
<p>欢迎{username}</p>
2. 注意0的渲染问题
用&&
运算时,如果条件是数字0,会意外渲染0(因为0是falsy值,但React会渲染数字):
const count = 0;// 错误:会显示"0",而不是不显示
{count && <p>数量:{count}</p>}// 正确:明确判断count > 0
{count > 0 && <p>数量:{count}</p>}
3. 避免多层嵌套的三元表达式
多层嵌套的三元表达式可读性极差,建议用if-else
或变量拆分:
// 不好的写法:多层嵌套,难以理解
{status === 'loading' ? <Loading /> :status === 'success' ? <Success data={data} /> :<Error message={error} />
}// 更好的写法:用变量存储
let content;
if (status === 'loading') {content = <Loading />;
} else if (status === 'success') {content = <Success data={data} />;
} else {content = <Error message={error} />;
}{content}
五、综合案例:带状态的用户登录组件
实现一个完整的登录流程组件,包含:
- 未登录时显示登录表单(用户名输入框+登录按钮);
- 登录中显示"登录中…";
- 登录成功显示欢迎信息和注销按钮;
- 登录失败显示错误提示。
import { useState } from 'react';function LoginForm() {// 状态管理:用户名、登录状态、错误信息const [username, setUsername] = useState('');const [status, setStatus] = useState('idle'); // idle/loading/success/errorconst [error, setError] = useState('');const handleLogin = () => {if (!username.trim()) {setError('请输入用户名');return;}// 模拟登录请求(2秒后完成)setStatus('loading');setTimeout(() => {// 模拟登录成功(实际项目中根据接口返回判断)setStatus('success');setError('');}, 2000);};const handleLogout = () => {setStatus('idle');setUsername('');};// 根据状态渲染不同内容if (status === 'idle') {return (<div><inputtype="text"placeholder="请输入用户名"value={username}onChange={(e) => setUsername(e.target.value)}/><button onClick={handleLogin}>登录</button>{error && <p style={{ color: 'red' }}>{error}</p>}</div>);} else if (status === 'loading') {return <p>登录中...</p>;} else if (status === 'success') {return (<div><p>欢迎,{username}!</p><button onClick={handleLogout}>注销</button></div>);} else if (status === 'error') {return (<div><p style={{ color: 'red' }}>{error}</p><button onClick={() => setStatus('idle')}>重新登录</button></div>);}
}export default LoginForm;
这个案例综合了State管理、事件处理和条件渲染,模拟了真实登录流程中的状态变化,展示了条件渲染在实际场景中的应用。