Appearance
列表项的增删改
学习目标
通过本章学习,你将全面掌握:
- 列表项的添加操作(开头/末尾/指定位置)
- 列表项的删除操作(单个/批量/条件删除)
- 列表项的更新操作(单属性/多属性/批量更新)
- 列表的排序和过滤
- 列表的拖拽排序
- 批量操作的实现
- 性能优化策略
- React 19中的最佳实践
第一部分:添加列表项
1.1 添加到末尾
jsx
function AddToEnd() {
const [items, setItems] = useState([
{ id: 1, text: '项目1', createdAt: new Date() },
{ id: 2, text: '项目2', createdAt: new Date() }
]);
const [inputValue, setInputValue] = useState('');
const addItem = () => {
if (inputValue.trim()) {
const newItem = {
id: Date.now(),
text: inputValue,
createdAt: new Date()
};
// 使用展开运算符添加到末尾
setItems([...items, newItem]);
setInputValue('');
}
};
// 或使用concat
const addItemWithConcat = () => {
if (inputValue.trim()) {
const newItem = {
id: Date.now(),
text: inputValue,
createdAt: new Date()
};
setItems(items.concat(newItem));
setInputValue('');
}
};
return (
<div>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="输入内容"
/>
<button onClick={addItem}>添加到末尾</button>
<ul>
{items.map(item => (
<li key={item.id}>
{item.text} - {item.createdAt.toLocaleTimeString()}
</li>
))}
</ul>
</div>
);
}1.2 添加到开头
jsx
function AddToStart() {
const [items, setItems] = useState([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' }
]);
const addItem = () => {
const newItem = {
id: Date.now(),
text: '新项目'
};
// 添加到开头
setItems([newItem, ...items]);
};
// 或使用unshift的不可变版本
const addItemAlternative = () => {
const newItem = {
id: Date.now(),
text: '新项目'
};
setItems(prev => [newItem].concat(prev));
};
return (
<div>
<button onClick={addItem}>添加到开头</button>
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</div>
);
}1.3 插入到指定位置
jsx
function InsertAt() {
const [items, setItems] = useState([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' },
{ id: 3, text: 'C' },
{ id: 4, text: 'D' }
]);
const insertAt = (index, text) => {
const newItem = {
id: Date.now(),
text
};
// 在指定位置插入
setItems([
...items.slice(0, index),
newItem,
...items.slice(index)
]);
};
// 在某个项目后面插入
const insertAfter = (id, text) => {
const index = items.findIndex(item => item.id === id);
if (index !== -1) {
insertAt(index + 1, text);
}
};
// 在某个项目前面插入
const insertBefore = (id, text) => {
const index = items.findIndex(item => item.id === id);
if (index !== -1) {
insertAt(index, text);
}
};
return (
<div>
<button onClick={() => insertAt(0, '开头')}>在开头插入</button>
<button onClick={() => insertAt(2, '中间')}>在位置2插入</button>
<button onClick={() => insertAt(items.length, '末尾')}>在末尾插入</button>
<ul>
{items.map((item, index) => (
<li key={item.id}>
{item.text}
<button onClick={() => insertBefore(item.id, 'Before')}>
在前面插入
</button>
<button onClick={() => insertAfter(item.id, 'After')}>
在后面插入
</button>
</li>
))}
</ul>
</div>
);
}1.4 批量添加
jsx
function BatchAdd() {
const [items, setItems] = useState([]);
const [inputValue, setInputValue] = useState('');
// 批量添加(从文本解析)
const batchAdd = () => {
const lines = inputValue.split('\n').filter(line => line.trim());
const newItems = lines.map((line, index) => ({
id: Date.now() + index,
text: line.trim(),
createdAt: new Date()
}));
setItems(prev => [...prev, ...newItems]);
setInputValue('');
};
// 从JSON导入
const importFromJSON = () => {
try {
const data = JSON.parse(inputValue);
if (Array.isArray(data)) {
setItems(prev => [...prev, ...data]);
setInputValue('');
}
} catch (error) {
alert('JSON格式错误');
}
};
return (
<div>
<textarea
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="每行一个项目,或粘贴JSON数组"
rows={5}
/>
<button onClick={batchAdd}>批量添加</button>
<button onClick={importFromJSON}>从JSON导入</button>
<p>当前项目数: {items.length}</p>
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</div>
);
}第二部分:删除列表项
2.1 按ID删除
jsx
function DeleteById() {
const [items, setItems] = useState([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' },
{ id: 3, text: 'C' }
]);
const deleteItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
// 带确认的删除
const deleteWithConfirm = (id) => {
const item = items.find(i => i.id === id);
if (confirm(`确定要删除 "${item.text}" 吗?`)) {
deleteItem(id);
}
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.text}
<button onClick={() => deleteItem(item.id)}>
删除
</button>
<button onClick={() => deleteWithConfirm(item.id)}>
删除(确认)
</button>
</li>
))}
</ul>
);
}2.2 按索引删除
jsx
function DeleteByIndex() {
const [items, setItems] = useState(['A', 'B', 'C', 'D', 'E']);
// 删除指定索引
const deleteAt = (index) => {
setItems(items.filter((_, i) => i !== index));
};
// 删除第一个
const deleteFirst = () => {
setItems(items.slice(1));
};
// 删除最后一个
const deleteLast = () => {
setItems(items.slice(0, -1));
};
// 删除多个(从index开始删除count个)
const deleteMultiple = (index, count) => {
setItems([
...items.slice(0, index),
...items.slice(index + count)
]);
};
return (
<div>
<button onClick={deleteFirst}>删除第一个</button>
<button onClick={deleteLast}>删除最后一个</button>
<button onClick={() => deleteMultiple(1, 2)}>删除第2、3个</button>
<ul>
{items.map((item, index) => (
<li key={index}>
{item}
<button onClick={() => deleteAt(index)}>删除</button>
</li>
))}
</ul>
</div>
);
}2.3 条件删除
jsx
function ConditionalDelete() {
const [items, setItems] = useState([
{ id: 1, text: 'A', completed: false, important: true },
{ id: 2, text: 'B', completed: true, important: false },
{ id: 3, text: 'C', completed: false, important: false },
{ id: 4, text: 'D', completed: true, important: true }
]);
// 删除已完成的项目
const deleteCompleted = () => {
setItems(items.filter(item => !item.completed));
};
// 删除不重要的项目
const deleteUnimportant = () => {
setItems(items.filter(item => item.important));
};
// 删除满足条件的项目
const deleteWhere = (predicate) => {
setItems(items.filter(item => !predicate(item)));
};
return (
<div>
<button onClick={deleteCompleted}>删除已完成</button>
<button onClick={deleteUnimportant}>删除不重要</button>
<button onClick={() => deleteWhere(item => item.completed && !item.important)}>
删除已完成且不重要的
</button>
<ul>
{items.map(item => (
<li key={item.id}>
{item.text}
{item.completed && ' (已完成)'}
{item.important && ' (重要)'}
</li>
))}
</ul>
</div>
);
}2.4 批量删除
jsx
function BatchDelete() {
const [items, setItems] = useState([
{ id: 1, text: 'A', selected: false },
{ id: 2, text: 'B', selected: false },
{ id: 3, text: 'C', selected: false },
{ id: 4, text: 'D', selected: false }
]);
// 切换选中状态
const toggleSelect = (id) => {
setItems(items.map(item =>
item.id === id ? { ...item, selected: !item.selected } : item
));
};
// 全选/取消全选
const toggleAll = () => {
const allSelected = items.every(item => item.selected);
setItems(items.map(item => ({
...item,
selected: !allSelected
})));
};
// 删除选中项
const deleteSelected = () => {
const selectedCount = items.filter(item => item.selected).length;
if (selectedCount === 0) {
alert('请先选择项目');
return;
}
if (confirm(`确定要删除 ${selectedCount} 个项目吗?`)) {
setItems(items.filter(item => !item.selected));
}
};
const selectedCount = items.filter(item => item.selected).length;
const allSelected = items.length > 0 && items.every(item => item.selected);
return (
<div>
<div className="batch-actions">
<label>
<input
type="checkbox"
checked={allSelected}
onChange={toggleAll}
/>
全选
</label>
<button
onClick={deleteSelected}
disabled={selectedCount === 0}
>
删除选中项 ({selectedCount})
</button>
</div>
<ul>
{items.map(item => (
<li key={item.id}>
<input
type="checkbox"
checked={item.selected}
onChange={() => toggleSelect(item.id)}
/>
{item.text}
</li>
))}
</ul>
</div>
);
}第三部分:更新列表项
3.1 更新单个属性
jsx
function UpdateProperty() {
const [items, setItems] = useState([
{ id: 1, text: 'A', completed: false, priority: 'low' },
{ id: 2, text: 'B', completed: false, priority: 'medium' },
{ id: 3, text: 'C', completed: false, priority: 'high' }
]);
// 切换完成状态
const toggleComplete = (id) => {
setItems(items.map(item =>
item.id === id
? { ...item, completed: !item.completed }
: item
));
};
// 更新优先级
const updatePriority = (id, priority) => {
setItems(items.map(item =>
item.id === id
? { ...item, priority }
: item
));
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
<input
type="checkbox"
checked={item.completed}
onChange={() => toggleComplete(item.id)}
/>
<span style={{
textDecoration: item.completed ? 'line-through' : 'none'
}}>
{item.text}
</span>
<select
value={item.priority}
onChange={e => updatePriority(item.id, e.target.value)}
>
<option value="low">低</option>
<option value="medium">中</option>
<option value="high">高</option>
</select>
</li>
))}
</ul>
);
}3.2 更新文本内容
jsx
function UpdateText() {
const [items, setItems] = useState([
{ id: 1, text: 'A', editing: false },
{ id: 2, text: 'B', editing: false }
]);
const [editValue, setEditValue] = useState('');
// 开始编辑
const startEdit = (id, text) => {
setItems(items.map(item =>
item.id === id ? { ...item, editing: true } : item
));
setEditValue(text);
};
// 保存编辑
const saveEdit = (id) => {
setItems(items.map(item =>
item.id === id
? { ...item, text: editValue, editing: false }
: item
));
setEditValue('');
};
// 取消编辑
const cancelEdit = (id) => {
setItems(items.map(item =>
item.id === id ? { ...item, editing: false } : item
));
setEditValue('');
};
// 直接更新文本
const updateText = (id, newText) => {
setItems(items.map(item =>
item.id === id ? { ...item, text: newText } : item
));
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.editing ? (
<div>
<input
value={editValue}
onChange={e => setEditValue(e.target.value)}
autoFocus
/>
<button onClick={() => saveEdit(item.id)}>保存</button>
<button onClick={() => cancelEdit(item.id)}>取消</button>
</div>
) : (
<div>
<span>{item.text}</span>
<button onClick={() => startEdit(item.id, item.text)}>
编辑
</button>
<input
type="text"
onChange={e => updateText(item.id, e.target.value)}
placeholder="直接输入新值"
/>
</div>
)}
</li>
))}
</ul>
);
}3.3 更新多个属性
jsx
function UpdateMultipleProperties() {
const [items, setItems] = useState([
{
id: 1,
title: '任务1',
description: '描述1',
status: 'pending',
priority: 'low',
assignee: '',
dueDate: ''
}
]);
// 更新单个属性
const updateProperty = (id, property, value) => {
setItems(items.map(item =>
item.id === id
? { ...item, [property]: value }
: item
));
};
// 更新多个属性
const updateMultiple = (id, updates) => {
setItems(items.map(item =>
item.id === id
? { ...item, ...updates }
: item
));
};
// 标记为完成(更新多个属性)
const markAsComplete = (id) => {
updateMultiple(id, {
status: 'completed',
completedAt: new Date(),
completedBy: 'currentUser'
});
};
return (
<ul>
{items.map(item => (
<li key={item.id}>
<input
value={item.title}
onChange={e => updateProperty(item.id, 'title', e.target.value)}
/>
<textarea
value={item.description}
onChange={e => updateProperty(item.id, 'description', e.target.value)}
/>
<select
value={item.status}
onChange={e => updateProperty(item.id, 'status', e.target.value)}
>
<option value="pending">待处理</option>
<option value="in-progress">进行中</option>
<option value="completed">已完成</option>
</select>
<button onClick={() => markAsComplete(item.id)}>
标记为完成
</button>
</li>
))}
</ul>
);
}3.4 批量更新
jsx
function BatchUpdate() {
const [items, setItems] = useState([
{ id: 1, text: 'A', completed: false, archived: false },
{ id: 2, text: 'B', completed: false, archived: false },
{ id: 3, text: 'C', completed: false, archived: false }
]);
// 全部标记为完成
const completeAll = () => {
setItems(items.map(item => ({
...item,
completed: true,
completedAt: new Date()
})));
};
// 全部标记为未完成
const uncompleteAll = () => {
setItems(items.map(item => ({
...item,
completed: false,
completedAt: null
})));
};
// 归档已完成的
const archiveCompleted = () => {
setItems(items.map(item =>
item.completed
? { ...item, archived: true }
: item
));
};
// 批量更新满足条件的项目
const updateWhere = (predicate, updates) => {
setItems(items.map(item =>
predicate(item)
? { ...item, ...updates }
: item
));
};
return (
<div>
<div className="batch-actions">
<button onClick={completeAll}>全部完成</button>
<button onClick={uncompleteAll}>全部未完成</button>
<button onClick={archiveCompleted}>归档已完成</button>
<button onClick={() => updateWhere(
item => item.text.includes('重要'),
{ priority: 'high' }
)}>
标记"重要"为高优先级
</button>
</div>
<ul>
{items.map(item => (
<li key={item.id}>
<input
type="checkbox"
checked={item.completed}
readOnly
/>
{item.text}
{item.archived && ' (已归档)'}
</li>
))}
</ul>
</div>
);
}第四部分:复杂操作
4.1 拖拽排序
jsx
function DragSortList() {
const [items, setItems] = useState([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' },
{ id: 3, text: 'C' },
{ id: 4, text: 'D' }
]);
const [draggedId, setDraggedId] = useState(null);
const [overIndex, setOverIndex] = useState(null);
const handleDragStart = (id) => (e) => {
setDraggedId(id);
e.dataTransfer.effectAllowed = 'move';
};
const handleDragOver = (index) => (e) => {
e.preventDefault();
setOverIndex(index);
};
const handleDrop = (dropIndex) => (e) => {
e.preventDefault();
if (draggedId === null) return;
const dragIndex = items.findIndex(item => item.id === draggedId);
if (dragIndex === -1 || dragIndex === dropIndex) return;
const newItems = [...items];
const [draggedItem] = newItems.splice(dragIndex, 1);
newItems.splice(dropIndex, 0, draggedItem);
setItems(newItems);
setDraggedId(null);
setOverIndex(null);
};
const handleDragEnd = () => {
setDraggedId(null);
setOverIndex(null);
};
// 使用按钮移动
const moveUp = (id) => {
const index = items.findIndex(item => item.id === id);
if (index > 0) {
const newItems = [...items];
[newItems[index - 1], newItems[index]] = [newItems[index], newItems[index - 1]];
setItems(newItems);
}
};
const moveDown = (id) => {
const index = items.findIndex(item => item.id === id);
if (index < items.length - 1) {
const newItems = [...items];
[newItems[index], newItems[index + 1]] = [newItems[index + 1], newItems[index]];
setItems(newItems);
}
};
return (
<ul>
{items.map((item, index) => (
<li
key={item.id}
draggable
onDragStart={handleDragStart(item.id)}
onDragOver={handleDragOver(index)}
onDrop={handleDrop(index)}
onDragEnd={handleDragEnd}
style={{
padding: '10px',
margin: '5px 0',
background: draggedId === item.id ? '#e0e0e0' :
overIndex === index ? '#f0f0f0' : 'white',
border: '1px solid #ccc',
cursor: 'move'
}}
>
<span>{item.text}</span>
<div className="move-buttons">
<button
onClick={() => moveUp(item.id)}
disabled={index === 0}
>
↑
</button>
<button
onClick={() => moveDown(item.id)}
disabled={index === items.length - 1}
>
↓
</button>
</div>
</li>
))}
</ul>
);
}4.2 撤销/重做功能
jsx
function UndoRedoList() {
const [items, setItems] = useState([
{ id: 1, text: 'A' },
{ id: 2, text: 'B' }
]);
const [history, setHistory] = useState([[
{ id: 1, text: 'A' },
{ id: 2, text: 'B' }
]]);
const [historyIndex, setHistoryIndex] = useState(0);
// 添加到历史记录
const addToHistory = (newItems) => {
const newHistory = history.slice(0, historyIndex + 1);
newHistory.push(newItems);
setHistory(newHistory);
setHistoryIndex(newHistory.length - 1);
setItems(newItems);
};
// 撤销
const undo = () => {
if (historyIndex > 0) {
const newIndex = historyIndex - 1;
setHistoryIndex(newIndex);
setItems(history[newIndex]);
}
};
// 重做
const redo = () => {
if (historyIndex < history.length - 1) {
const newIndex = historyIndex + 1;
setHistoryIndex(newIndex);
setItems(history[newIndex]);
}
};
// 操作函数
const addItem = (text) => {
const newItems = [
...items,
{ id: Date.now(), text }
];
addToHistory(newItems);
};
const deleteItem = (id) => {
const newItems = items.filter(item => item.id !== id);
addToHistory(newItems);
};
const updateItem = (id, text) => {
const newItems = items.map(item =>
item.id === id ? { ...item, text } : item
);
addToHistory(newItems);
};
return (
<div>
<div className="history-controls">
<button onClick={undo} disabled={historyIndex === 0}>
↶ 撤销
</button>
<span>{historyIndex + 1} / {history.length}</span>
<button onClick={redo} disabled={historyIndex === history.length - 1}>
↷ 重做
</button>
</div>
<button onClick={() => addItem('New Item')}>
添加项目
</button>
<ul>
{items.map(item => (
<li key={item.id}>
<input
value={item.text}
onChange={e => updateItem(item.id, e.target.value)}
/>
<button onClick={() => deleteItem(item.id)}>
删除
</button>
</li>
))}
</ul>
</div>
);
}4.3 列表排序
jsx
function SortableList() {
const [items, setItems] = useState([
{ id: 1, name: 'Charlie', age: 25, score: 85 },
{ id: 2, name: 'Alice', age: 30, score: 95 },
{ id: 3, name: 'Bob', age: 28, score: 90 }
]);
const [sortBy, setSortBy] = useState('name');
const [sortOrder, setSortOrder] = useState('asc');
// 排序函数
const sortedItems = useMemo(() => {
return [...items].sort((a, b) => {
let compareValue = 0;
if (sortBy === 'name') {
compareValue = a.name.localeCompare(b.name);
} else if (sortBy === 'age') {
compareValue = a.age - b.age;
} else if (sortBy === 'score') {
compareValue = a.score - b.score;
}
return sortOrder === 'asc' ? compareValue : -compareValue;
});
}, [items, sortBy, sortOrder]);
// 切换排序顺序
const toggleSortOrder = () => {
setSortOrder(order => order === 'asc' ? 'desc' : 'asc');
};
// 应用排序(修改原数组)
const applySort = () => {
setItems(sortedItems);
};
return (
<div>
<div className="sort-controls">
<select value={sortBy} onChange={e => setSortBy(e.target.value)}>
<option value="name">按名称</option>
<option value="age">按年龄</option>
<option value="score">按分数</option>
</select>
<button onClick={toggleSortOrder}>
{sortOrder === 'asc' ? '升序 ↑' : '降序 ↓'}
</button>
<button onClick={applySort}>
应用排序
</button>
</div>
<table>
<thead>
<tr>
<th onClick={() => setSortBy('name')} style={{ cursor: 'pointer' }}>
名称 {sortBy === 'name' && (sortOrder === 'asc' ? '↑' : '↓')}
</th>
<th onClick={() => setSortBy('age')} style={{ cursor: 'pointer' }}>
年龄 {sortBy === 'age' && (sortOrder === 'asc' ? '↑' : '↓')}
</th>
<th onClick={() => setSortBy('score')} style={{ cursor: 'pointer' }}>
分数 {sortBy === 'score' && (sortOrder === 'asc' ? '↑' : '↓')}
</th>
</tr>
</thead>
<tbody>
{sortedItems.map(item => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.age}</td>
<td>{item.score}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}第五部分:性能优化
5.1 使用useCallback优化
jsx
function OptimizedList() {
const [items, setItems] = useState([]);
// 缓存操作函数
const addItem = useCallback((text) => {
setItems(prev => [
...prev,
{ id: Date.now(), text }
]);
}, []);
const deleteItem = useCallback((id) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
const updateItem = useCallback((id, text) => {
setItems(prev => prev.map(item =>
item.id === id ? { ...item, text } : item
));
}, []);
const toggleItem = useCallback((id) => {
setItems(prev => prev.map(item =>
item.id === id ? { ...item, completed: !item.completed } : item
));
}, []);
return (
<div>
<button onClick={() => addItem('New')}>添加</button>
<ul>
{items.map(item => (
<MemoListItem
key={item.id}
item={item}
onDelete={deleteItem}
onUpdate={updateItem}
onToggle={toggleItem}
/>
))}
</ul>
</div>
);
}
const MemoListItem = React.memo(function ListItem({
item,
onDelete,
onUpdate,
onToggle
}) {
console.log('ListItem渲染:', item.id);
return (
<li>
<input
type="checkbox"
checked={item.completed}
onChange={() => onToggle(item.id)}
/>
<input
value={item.text}
onChange={e => onUpdate(item.id, e.target.value)}
/>
<button onClick={() => onDelete(item.id)}>删除</button>
</li>
);
});5.2 虚拟化大列表
jsx
import { FixedSizeList } from 'react-window';
function VirtualizedEditableList() {
const [items, setItems] = useState(
Array.from({ length: 10000 }, (_, i) => ({
id: i,
text: `Item ${i}`,
value: Math.random()
}))
);
const deleteItem = useCallback((id) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
const updateItem = useCallback((id, updates) => {
setItems(prev => prev.map(item =>
item.id === id ? { ...item, ...updates } : item
));
}, []);
const Row = useCallback(({ index, style }) => {
const item = items[index];
return (
<div style={style}>
<EditableRow
item={item}
onDelete={deleteItem}
onUpdate={updateItem}
/>
</div>
);
}, [items, deleteItem, updateItem]);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
const EditableRow = React.memo(function EditableRow({ item, onDelete, onUpdate }) {
return (
<div style={{ display: 'flex', alignItems: 'center', padding: '0 10px' }}>
<input
value={item.text}
onChange={e => onUpdate(item.id, { text: e.target.value })}
style={{ flex: 1 }}
/>
<button onClick={() => onDelete(item.id)}>删除</button>
</div>
);
});第六部分:React 19增强
6.1 使用useOptimistic
jsx
'use client';
import { useOptimistic } from 'react';
function OptimisticList({ initialItems }) {
const [items, setItems] = useState(initialItems);
const [optimisticItems, addOptimisticUpdate] = useOptimistic(
items,
(state, { action, id, data }) => {
switch (action) {
case 'add':
return [...state, data];
case 'delete':
return state.filter(item => item.id !== id);
case 'update':
return state.map(item =>
item.id === id ? { ...item, ...data } : item
);
default:
return state;
}
}
);
const handleAdd = async (text) => {
const newItem = { id: Date.now(), text };
addOptimisticUpdate({ action: 'add', data: newItem });
await saveItem(newItem);
setItems(prev => [...prev, newItem]);
};
const handleDelete = async (id) => {
addOptimisticUpdate({ action: 'delete', id });
await deleteItem(id);
setItems(prev => prev.filter(item => item.id !== id));
};
return (
<ul>
{optimisticItems.map(item => (
<li key={item.id}>
{item.text}
<button onClick={() => handleDelete(item.id)}>删除</button>
</li>
))}
</ul>
);
}练习题
基础练习
- 实现列表项的添加、删除功能
- 创建可编辑的列表项
- 实现批量选择和删除
进阶练习
- 实现列表的排序功能
- 创建可拖拽排序的列表
- 实现列表的搜索和过滤
- 添加撤销/重做功能
高级练习
- 优化大列表的增删改性能
- 实现虚拟化的可编辑列表
- 创建复杂的数据表格编辑器
- 使用React 19的useOptimistic实现乐观更新
通过本章学习,你已经掌握了列表项的增删改全部操作。这些是构建动态列表应用的基础技能。继续学习,探索更多列表操作技巧!