提升 React:精通列表、键和组件模式!(React 第4天)
抱歉,我需要您提供要翻译的具体文本内容(文章正文),才能为您完成翻译。请把需要翻译的部分粘贴在这里,我会按照要求保留源链接、格式和代码块进行翻译。
React 中的列表
工作原理
使用像 map() 这样的数组方法将数据转换为 JSX。每个项目都会变成一个组件或元素。
实用示例:基础待办列表
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState(['Buy milk', 'Walk dog', 'Code React']);
return (
{todos.map((todo, index) => (
- {todo}
{/* Keys coming up next! */}
))}
);
}
这会渲染一个无序列表。你可以通过表单等方式添加状态更新,以实现动态效果。
UI 场景
在购物车应用中:
{cart.map(item => (
))}
用户可以添加/移除商品,界面会无缝更新。
常见错误
忘记处理空列表可能导致界面空白。添加条件判断:
{todos.length ? (
todos.map((todo, i) => - {todo}
)
) : (
No todos!
)}
最佳实践
保持映射逻辑简洁;将复杂的项提取为子组件,以提升可读性。
键
键是通过 key 属性分配给列表项的唯一字符串(或数字)。它们 不会 传递给组件;而是被 React 在内部使用。
为什么键在内部很重要
在调和(React 的 diff 算法)过程中,键帮助识别哪些项目被添加、删除或更改。没有稳定的键,React 会退回使用基于索引的匹配,这可能导致更新错误。
性能影响
使用合适的键可以最小化 DOM 操作——React 能复用元素而不是重新创建它们。错误的键会导致不必要的重新渲染、界面闪烁以及组件状态丢失(例如输入框焦点)。
实际示例:带有稳定键的动态列表
function UserList({ users }) {
return (
{users.map(user => (
- {user.name}
{/* Use stable ID like a database key */}
))}
);
}
如果 users 的顺序被重新排列,键能够确保平滑的过渡。
常见错误
- 使用索引作为键 – 仅在静态列表中可接受;在可排序/可过滤的列表中会导致灾难。
- 键重复 – React 会给出警告;键必须在同级兄弟节点中唯一。
- 没有键 – 控制台警告并且渲染效率低下。
UI 场景
在聊天应用中:
{messages.map(msg => (
))}
如果没有合适的键,滚动或新消息可能会导致焦点重置或动画中断。
最佳实践
使用稳定、唯一的 ID(例如来自 API 的数据)。避免在每次渲染时都会变化的随机键。
Source: …
组件模式
可复用的组件受益于清晰的模式,这些模式促进模块化和关注点分离。
常见模式
- 表现层 vs. 容器层(Smart vs. Dumb) – 表现层组件负责 UI;容器层组件管理逻辑(状态、数据获取),并向下传递数据。
- 复合组件 – 将共享隐式状态的相关组件组合在一起(例如
…)。 - 渲染属性 / 高阶组件(Render Props / HOCs) – 高级复用技术;hooks 通常提供更简洁的替代方案。
实践示例:容器‑表现层拆分
// Presentational: Pure UI
function UserDisplay({ user }) {
return Name: {user.name}, Age: {user.age};
}
// Container: Logic
import { useState, useEffect } from 'react';
function UserContainer() {
const [user, setUser] = useState(null);
useEffect(() => {
// Simulate fetch
setUser({ name: 'Alex', age: 28 });
}, []);
return user ? :
Loading...
;
}
UI 场景
一个仪表盘可能有一个 ChartContainer 用于获取数据,并将数据传递给 ChartDisplay 组件,从而使相同的展示逻辑能够在不同查询的页面之间复用。
最佳实践
从简单开始;随着组件的增长再重构为相应的模式。TypeScript 可以帮助强制可复用组件的属性契约。
关注点分离
将职责划分——UI 与逻辑、状态 与 渲染——可以让测试、调试和协作更容易。
如何应用
- 提取逻辑 到自定义 Hook 或工具模块。
- 保持组件专注:一个组件负责获取数据,另一个负责渲染。
实际示例
上面的 UserContainer 将数据获取与展示分离。相同的原则适用于列表渲染:一个 Hook 获取用户数据,而 UserList 只负责将其映射为 JSX。
常见错误
单块组件同时处理获取、状态和 UI,会导致难以复用或测试。应及早拆分。
UI 场景
在新闻推送中:
FeedContainer负责 API 调用、分页和错误处理。FeedItem仅渲染文章。
你可以在不触及数据获取逻辑的情况下更换展示组件。
最佳实践
采用“每个文件只关注一个职责”的方法:
src/
├─ components/
│ ├─ containers/
│ └─ presentational/
└─ hooks/
这种结构强化了明确的边界并促进了可复用性。