6. 列表渲染与条件渲染
这是什么:列表与条件渲染 = 在 JSX 里用 map 生成列表、用三元或 && 决定某段 UI 要不要显示。
为什么用:上一章的页面写死着一条内容,真正实用的页面得能列出一堆数据、还得按情况显示或隐藏,JSX 里写 JS 正好顺手。
如何用:本章先把任务数组用 map 渲染成列表并补上 key,再做一个筛搜页,最后在一个页里把三种条件写法一次跑通。
6.1 列表:map 一把梭
整段可跑:
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
<script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
<script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
<div id='root'></div>
<script type='text/babel' data-presets='react'>
function App() {
const tasks = [
{ id: 1, text: '学 React', done: false },
{ id: 2, text: '写教程', done: true }
]
return (
<ul>
{tasks.map((task) => (
<li key={task.id}>{task.done ? '✅ ' : '⬜ '}{task.text}</li> // key 用稳定 id,别用下标
))}
</ul>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
</script>
</body>
</html>
你会看到:「⬜ 学 React」「✅ 写教程」两条列表。
两个关键点:
arr.map()返回一串 JSX,插进{ }即可- key 必须,让 React 认得「同一行」(用稳定 id,别用 index——中途插入会错乱)
6.2 条件渲染:三元与 && 当场各跑各
整段可跑,三种姿势一次看全(「提前 return」写在读法说明里):
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
<script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
<script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
<div id='root'></div>
<script type='text/babel' data-presets='react'>
function App() {
const [isLogin, setIsLogin] = React.useState(true)
const [unread, setUnread] = React.useState(3)
return (
<div>
<h2>{isLogin ? '欢迎回来 👋' : '请先登录'}</h2> {/* ① 三元:二选一 */}
<button onClick={() => setIsLogin(!isLogin)}>
{isLogin ? '退出登录' : '去登录'}
</button>
{unread > 0 && <span>✉️ 未读 {unread}</span>} {/* ② && 短路:有才渲染 */}
<button onClick={() => setUnread(0)}>把未读清零试试</button>
<hr />
<p>关键词:{unread === 0 ? '未读清空' : '还有未读'}</p> {/* ③ 放心嵌三元 */}
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
</script>
</body>
</html>
你会看到:「欢迎回来 + 未读 3」,点「退出登录」整句变「请先登录」(三元切换),点「清零」徽标直接消失不再渲染——0 坑的防法就在这里:写 unread > 0 && 而不是 unread &&。
第三种姿势「提前 return」是整个函数分支:在 return 前先判 if (loading) return <p>加载中…</p>,走不到的正常分支再返回整页——适合 loading / error / 正常这类整页级分支。
三者场景:
| 姿势 | 适用 |
|---|---|
| 三元 | A 或 B 互斥 |
&& |
只有「显示 A 或不显示」 |
| 提前 return | 整页分支(loading / error / 正常) |
头号大坑:
&&左侧是数字时小心 0:{cnt && <Badge/>}当 cnt=0 会渲染出「0」字样。安全写法:{cnt > 0 && <Badge/>}。
6.3 筛一筛再展示:filter + map 组合拳
「搜一筛一展示」的真实页面,整段可跑:
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset='utf-8'>
<script src='https://registry.npmmirror.com/react/18.3.1/files/umd/react.production.min.js'></script>
<script src='https://registry.npmmirror.com/react-dom/18.3.1/files/umd/react-dom.production.min.js'></script>
<script src='https://registry.npmmirror.com/@babel/standalone/7.29.11/files/babel.min.js'></script>
</head>
<body>
<div id='root'></div>
<script type='text/babel' data-presets='react'>
function App() {
const tasks = [
{ id: 1, text: '学 React', done: false },
{ id: 2, text: '写教程', done: true },
{ id: 3, text: '跑步三公里', done: false }
]
const [keyword, setKeyword] = React.useState('')
const list = tasks
.filter((t) => t.text.includes(keyword)) // 筛
.filter((t) => !t.done) // 只要未完成
return (
<div>
<input
placeholder='搜任务'
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
{list.length === 0 && <p>没有匹配任务</p>}
<ul>
{list.map((t) => <li key={t.id}>{t.text}</li>)}
</ul>
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />)
</script>
</body>
</html>
你会看到:默认两条未完成任务;输入「跑」只剩一条;输入「写」什么都不显示(写教程已完成),换成「没有匹配任务」。
先纯 JS 筛好数据,再 map 出 UI——数据和视图解耦,代码慢慢读都不乱。
6.4 动手改
- 给 6.3 加第二个筛选:「只看已完成」(切换布尔,filter 一行换
t.done) - 列表为空时显示emoji空状态,替换「没有匹配任务」
- 渲染商品数组,价格超出 100 的加
<em>贵</em>标记
6.5 小结
- 列表 =
map+key(稳定 id 苦口婆心) - 条件 = 三元 /
&&(注意 0)/ 提前 return - 先把数据处理干净,再一口气 map 出 JSX
下一章:组件通信:props 🐸