动态数据展示详解
列表渲染:在React中,我们使用JavaScript的map()方法来渲染列表。map方法会将数组中的每个元素转换为一个React元素。
function NumberList() {
const numbers = [1, 2, 3, 4, 5];
return (
<ul>
{numbers.map((number) => (
<li key={number}>{number}</li>
))}
</ul>
);
}
function UserList() {
const users = [
{ id: 1, name: '张三', age: 25 },
{ id: 2, name: '李四', age: 30 },
{ id: 3, name: '王五', age: 28 }
];
return (
<div>
<h2>用户列表</h2>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} - {user.age}岁
</li>
))}
</ul>
</div>
);
}
Key的作用:Key是React用来识别哪些元素改变了、添加了或被移除的特殊属性。它帮助React高效地更新DOM。
重要警告:如果没有Key,React会使用索引作为默认值,这会导致性能问题和潜在的bug。
// 没有Key的情况(不推荐)
function BadList() {
const items = ['A', 'B', 'C'];
return (
<ul>
{items.map((item, index) => (
<li>{item}</li> // 没有Key,使用索引
))}
</ul>
);
}
// 有Key的情况(推荐)
function GoodList() {
const items = [
{ id: 1, text: 'A' },
{ id: 2, text: 'B' },
{ id: 3, text: 'C' }
];
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.text}</li> // 使用唯一ID作为Key
))}
</ul>
);
}
选择Key的原则:
function BestKeyExample() {
const todos = [
{ id: 'todo-1', text: '学习React' },
{ id: 'todo-2', text: '写代码' },
{ id: 'todo-3', text: '阅读文档' }
];
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
</li>
))}
</ul>
);
}
function IndexKeyExample() {
// 静态列表,不会重新排序或过滤
const staticItems = ['首页', '关于', '联系'];
return (
<nav>
{staticItems.map((item, index) => (
<a key={index} href={`#${item}`}>
{item}
</a>
))}
</nav>
);
}
避免使用索引的场景:
function GeneratedKeyExample() {
const items = ['苹果', '香蕉', '橙子'];
return (
<ul>
{items.map((item, index) => (
<li key={`${item}-${index}`}>
{item}
</li>
))}
</ul>
);
}
function FilteredList() {
const users = [
{ id: 1, name: '张三', age: 25, active: true },
{ id: 2, name: '李四', age: 30, active: false },
{ id: 3, name: '王五', age: 28, active: true }
];
// 只显示活跃用户
const activeUsers = users.filter(user => user.active);
return (
<div>
<h2>活跃用户</h2>
<ul>
{activeUsers.map(user => (
<li key={user.id}>
{user.name} - {user.age}岁
</li>
))}
</ul>
</div>
);
}
function SortedList() {
const products = [
{ id: 1, name: 'iPhone', price: 5999 },
{ id: 2, name: 'MacBook', price: 9999 },
{ id: 3, name: 'iPad', price: 3299 }
];
// 按价格排序
const sortedProducts = [...products].sort((a, b) => a.price - b.price);
return (
<div>
<h2>商品列表(按价格排序)</h2>
<ul>
{sortedProducts.map(product => (
<li key={product.id}>
{product.name} - ¥{product.price}
</li>
))}
</ul>
</div>
);
}
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: '学习React', completed: false },
{ id: 2, text: '写代码', completed: true }
]);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim() === '') return;
const todo = {
id: Date.now(),
text: newTodo,
completed: false
};
setTodos([...todos, todo]);
setNewTodo('');
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
return (
<div>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="添加新任务"
/>
<button onClick={addTodo}>添加</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>
删除
</button>
</li>
))}
</ul>
</div>
);
}
function NestedList() {
const categories = [
{
id: 1,
name: '水果',
items: ['苹果', '香蕉', '橙子']
},
{
id: 2,
name: '蔬菜',
items: ['胡萝卜', '白菜', '西红柿']
}
];
return (
<div>
{categories.map(category => (
<div key={category.id}>
<h3>{category.name}</h3>
<ul>
{category.items.map((item, index) => (
<li key={`${category.id}-${index}`}>
{item}
</li>
))}
</ul>
</div>
))}
</div>
);
}
function TableExample() {
const data = [
{ id: 1, name: '张三', age: 25, city: '北京' },
{ id: 2, name: '李四', age: 30, city: '上海' },
{ id: 3, name: '王五', age: 28, city: '广州' }
];
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>年龄</th>
<th>城市</th>
</tr>
</thead>
<tbody>
{data.map(row => (
<tr key={row.id}>
<td>{row.id}</td>
<td>{row.name}</td>
<td>{row.age}</td>
<td>{row.city}</td>
</tr>
))}
</tbody>
</table>
);
}
function EmptyListExample() {
const [items, setItems] = useState([]);
return (
<div>
{items.length === 0 ? (
<p>列表为空</p>
) : (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
)}
</div>
);
}
function SearchFilter() {
const [searchTerm, setSearchTerm] = useState('');
const users = [
{ id: 1, name: '张三', email: 'zhangsan@example.com' },
{ id: 2, name: '李四', email: 'lisi@example.com' },
{ id: 3, name: '王五', email: 'wangwu@example.com' }
];
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
user.email.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="搜索用户..."
/>
{filteredUsers.length === 0 ? (
<p>没有找到匹配的用户</p>
) : (
<ul>
{filteredUsers.map(user => (
<li key={user.id}>
{user.name} ({user.email})
</li>
))}
</ul>
)}
</div>
);
}
性能优化建议:
import { memo } from 'react';
// 使用memo优化列表项组件
const ListItem = memo(function ListItem({ item, onDelete }) {
console.log(`渲染: ${item.text}`);
return (
<li>
{item.text}
<button onClick={() => onDelete(item.id)}>
删除
</button>
</li>
);
});
function OptimizedList() {
const [items, setItems] = useState([
{ id: 1, text: '项目1' },
{ id: 2, text: '项目2' },
{ id: 3, text: '项目3' }
]);
const deleteItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
return (
<ul>
{items.map(item => (
<ListItem
key={item.id}
item={item}
onDelete={deleteItem}
/>
))}
</ul>
);
}
任务:创建一个任务管理应用
要求:
// 参考答案
function TaskManager() {
const [tasks, setTasks] = useState([
{ id: 1, text: '学习React', completed: false },
{ id: 2, text: '写代码', completed: true },
{ id: 3, text: '阅读文档', completed: false }
]);
const [newTask, setNewTask] = useState('');
const [filter, setFilter] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const addTask = () => {
if (newTask.trim() === '') return;
const task = {
id: Date.now(),
text: newTask,
completed: false
};
setTasks([...tasks, task]);
setNewTask('');
};
const deleteTask = (id) => {
setTasks(tasks.filter(task => task.id !== id));
};
const toggleTask = (id) => {
setTasks(tasks.map(task =>
task.id === id
? { ...task, completed: !task.completed }
: task
));
};
const filteredTasks = tasks.filter(task => {
const matchesFilter =
filter === 'all' ||
(filter === 'completed' && task.completed) ||
(filter === 'active' && !task.completed);
const matchesSearch = task.text
.toLowerCase()
.includes(searchTerm.toLowerCase());
return matchesFilter && matchesSearch;
});
const completedCount = tasks.filter(task => task.completed).length;
const activeCount = tasks.length - completedCount;
return (
<div>
<h2>任务管理</h2>
<div>
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="添加新任务"
/>
<button onClick={addTask}>添加</button>
</div>
<div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="搜索任务..."
/>
</div>
<div>
<button
onClick={() => setFilter('all')}
style={{ fontWeight: filter === 'all' ? 'bold' : 'normal' }}
>
全部 ({tasks.length})
</button>
<button
onClick={() => setFilter('active')}
style={{ fontWeight: filter === 'active' ? 'bold' : 'normal' }}
>
未完成 ({activeCount})
</button>
<button
onClick={() => setFilter('completed')}
style={{ fontWeight: filter === 'completed' ? 'bold' : 'normal' }}
>
已完成 ({completedCount})
</button>
</div>
<ul>
{filteredTasks.map(task => (
<li key={task.id}>
<input
type="checkbox"
checked={task.completed}
onChange={() => toggleTask(task.id)}
/>
<span style={{
textDecoration: task.completed ? 'line-through' : 'none'
}}>
{task.text}
</span>
<button onClick={() => deleteTask(task.id)}>
删除
</button>
</li>
))}
</ul>
{filteredTasks.length === 0 && (
<p>没有找到任务</p>
)}
</div>
);
}