function NumberList({ numbers }) {
const listItems = numbers.map((number) =>
{number}
);
return {listItems}
;
}
// 更简洁的写法
function NumberList({ numbers }) {
return (
{numbers.map(number => - {number}
)}
);
}
// 使用
const numbers = [1, 2, 3, 4, 5];
===== 9.2 Key 的重要性 =====
Key 帮助 React 识别哪些元素改变了、添加了或删除了。
function TodoList({ todos }) {
return (
{todos.map(todo => (
- {todo.text}
))}
;
);
}
**选择 Key 的最佳实践**:
* 使用数据中的唯一标识(如 ID)
* 避免使用数组索引(除非列表不会重新排序)
* 保持 Key 在同一兄弟元素中唯一
**为什么不能用索引作为 Key**:
// 错误示例
function List({ items }) {
return (
{items.map((item, index) => (
))}
;
);
}
// 问题:删除第一个元素后,所有元素的 key 都变了
// 导致 React 重新渲染所有输入框,丢失焦点状态
===== 9.3 提取列表组件 =====
function ListItem({ item }) {
return {item.text} ;
}
function TodoList({ todos }) {
return (
{todos.map(todo => (
))}
;
);
}
**注意**:key 应该放在数组上下文的元素上,而不是组件内部。
===== 9.4 嵌套列表 =====
function CategoryList({ categories }) {
return (
{categories.map(category => (
{category.name}
{category.items.map(item => (
- {item.name}
))}
))}
;
);
}
===== 9.5 列表过滤和排序 =====
**过滤列表**:
function FilteredList({ items, filterText }) {
const filtered = items.filter(item =>
item.name.toLowerCase().includes(filterText.toLowerCase())
);
return (
{filtered.map(item => (
- {item.name}
))}
;
);
}
**排序列表**:
function SortedList({ items, sortBy }) {
const sorted = [...items].sort((a, b) => {
if (sortBy === 'name') {
return a.name.localeCompare(b.name);
}
if (sortBy === 'date') {
return new Date(b.date) - new Date(a.date);
}
return 0;
});
return (
{sorted.map(item => (
- {item.name}
))}
;
);
}
===== 9.6 虚拟列表 =====
当列表项很多时,使用虚拟列表只渲染可视区域的内容。
**使用 react-window**:
npm install react-window
import { FixedSizeList as List } from 'react-window';
function VirtualList({ items }) {
const Row = ({ index, style }) => (
{items[index].name}
;
);
return (
{Row}
;
);
}
**使用 react-virtualized**:
npm install react-virtualized
import { List } from 'react-virtualized';
function VirtualizedList({ items }) {
const rowRenderer = ({ key, index, style }) => (
{items[index].name}
;
);
return (
;
);
}
===== 9.7 无限滚动 =====
import { useState, useEffect, useRef, useCallback } from 'react';
function InfiniteList() {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const observer = useRef();
const lastItemRef = useCallback(node => {
if (loading) return;
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
setPage(prevPage => prevPage + 1);
}
});
if (node) observer.current.observe(node);
}, [loading]);
useEffect(() => {
setLoading(true);
fetchItems(page).then(newItems => {
setItems(prev => [...prev, ...newItems]);
setLoading(false);
});
}, [page]);
return (
{items.map((item, index) => (
{item.name}
;
))}
{loading && 加载中...
}
;
);
}
===== 9.8 列表动画 =====
**使用 react-flip-toolkit**:
npm install react-flip-toolkit
import { Flipper, Flipped } from 'react-flip-toolkit';
function AnimatedList({ items }) {
return (
i.id).join(',')}>
{items.map(item => (
- {item.name}
;
))}
;
;
);
}
**使用 framer-motion**:
npm install framer-motion
import { motion, AnimatePresence } from 'framer-motion';
function AnimatedList({ items, onRemove }) {
return (
{items.map(item => (
onRemove(item.id)}
>
{item.name}
;
))}
;
);
}
===== 9.9 总结 =====
本章介绍了列表渲染的各种技术:
* 基础列表渲染
* Key 的重要性和最佳实践
* 列表过滤和排序
* 虚拟列表优化
* 无限滚动
* 列表动画
处理列表是 React 开发的常见场景,掌握这些技巧对于构建高性能应用非常重要。