使用React和Material-UI构建TODO应用的前端UI
引言
在现代Web开发中,TODO列表应用是一个经典的示例,用于展示如何使用前端技术构建一个简单的任务管理工具。本文将详细介绍如何使用React框架和Material-UI库来构建一个TODO列表应用,并解释代码的各个部分,帮助读者理解其工作原理。
后端API请参考,使用Express.js和SQLite3构建简单TODO应用的后端API
环境准备
在开始之前,请确保你已经安装了以下工具和库:
- Node.js:确保你已经安装了Node.js,可以从Node.js官网下载并安装。
- npm:Node.js的包管理工具,随Node.js一起安装。
- React:一个用于构建用户界面的JavaScript库,可以通过npm安装。
- Material-UI:一个基于Material Design的React组件库,同样可以通过npm安装。
- axios:一个基于Promise的HTTP客户端,用于处理API请求。
安装所需的依赖:
npm install react @mui/material @emotion/react @emotion/styled axios
代码解析
让我们逐步分析代码,理解每个部分的功能。
1. 导入必要的模块
import { useState, useEffect } from 'react';
import axios from 'axios';
import {
TextField,
Button,
Checkbox,
List,
ListItem,
ListItemText,
IconButton,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
FormControlLabel
} from '@mui/material';
import { Delete, Edit } from '@mui/icons-material';
import { Todo } from '../types/todo';
- React:导入
useState
和useEffect
钩子,用于状态管理和副作用处理。 - axios:用于发送HTTP请求。
- Material-UI:导入各种组件,如文本框、按钮、列表、对话框等。
- Icons:导入删除和编辑图标。
- Todo类型:定义TODO项的类型,确保数据的类型安全。
2. 创建React组件
export default function TodoList() {
const [todos, setTodos] = useState<Todo[]>([]);
const [searchTerm, setSearchTerm] = useState('');
const [newTodo, setNewTodo] = useState({ title: '', description: '' });
const [editingTodo, setEditingTodo] = useState<Todo | null>(null);
const [editForm, setEditForm] = useState({ title: '', description: '', completed: false });
- todos:存储TODO列表的状态。
- searchTerm:存储搜索词的状态。
- newTodo:存储新TODO项的状态。
- editingTodo:存储正在编辑的TODO项的状态。
- editForm:存储编辑表单的状态。
3. 定义函数
3.1 获取TODO列表
const fetchTodos = async () => {
try {
const response = await axios.get(`http://localhost:3002/api/todos?q=${searchTerm}`);
setTodos(response.data);
} catch (error) {
console.error('Error fetching todos:', error);
}
};
- 功能:从后端获取TODO列表,支持搜索功能。
- 实现:使用
axios.get
发送GET请求,根据searchTerm
进行模糊搜索。
3.2 创建TODO项
const createTodo = async () => {
if (!newTodo.title.trim()) return;
try {
await axios.post('http://localhost:3002/api/todos', newTodo);
setNewTodo({ title: '', description: '' });
fetchTodos();
} catch (error) {
console.error('Error creating todo:', error);
}
};
- 功能:创建一个新的TODO项。
- 实现:检查标题是否为空,使用
axios.post
发送POST请求,创建成功后清空表单并刷新列表。
3.3 更新TODO项
const updateTodo = async (todo: Todo, isToggleComplete = false) => {
try {
const updatedTodo = isToggleComplete
? { ...todo, completed: !todo.completed }
: { ...todo, title: editForm.title, description: editForm.description, completed: editForm.completed };
await axios.put(`http://localhost:3002/api/todos/${todo.id}`, updatedTodo);
setEditingTodo(null);
fetchTodos();
} catch (error) {
console.error('Error updating todo:', error);
}
};
- 功能:更新TODO项,支持标记完成和编辑内容。
- 实现:根据
isToggleComplete
决定是更新完成状态还是编辑内容,使用axios.put
发送PUT请求。
3.4 删除TODO项
const deleteTodo = async (id: number) => {
try {
await axios.delete(`http://localhost:3002/api/todos/${id}`);
fetchTodos();
} catch (error) {
console.error('Error deleting todo:', error);
}
};
- 功能:删除指定的TODO项。
- 实现:使用
axios.delete
发送DELETE请求,删除成功后刷新列表。
3.5 处理编辑点击事件
const handleEditClick = (todo: Todo) => {
setEditingTodo(todo);
setEditForm({
title: todo.title,
description: todo.description || '',
completed: todo.completed
});
};
- 功能:打开编辑对话框,填充TODO项的详细信息。
- 实现:设置
editingTodo
和editForm
状态,显示编辑表单。
3.6 关闭编辑对话框
const handleClose = () => {
setEditingTodo(null);
setEditForm({ title: '', description: '', completed: false });
};
- 功能:关闭编辑对话框,重置表单。
- 实现:清除
editingTodo
和editForm
状态。
3.7 保存编辑内容
const handleSave = () => {
if (editingTodo && editForm.title.trim()) {
updateTodo(editingTodo);
}
};
- 功能:保存编辑的内容。
- 实现:检查标题是否为空,调用
updateTodo
更新TODO项。
4. 使用Effect钩子
useEffect(() => {
const debounceSearch = setTimeout(() => {
fetchTodos();
}, 300);
return () => clearTimeout(debounceSearch);
}, [searchTerm]);
- 功能:实现搜索的防抖动效果,防止频繁请求。
- 实现:使用
setTimeout
延迟300毫秒后执行fetchTodos
,并在组件销毁时清除定时器。
5. 渲染组件
return (
<div style={{ maxWidth: 600, margin: '0 auto', padding: '20px' }}>
<TextField
fullWidth
label="Search Todos"
variant="outlined"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
margin="normal"
/>
<List>
{todos.map((todo) => (
<ListItem
key={todo.id}
secondaryAction={(
<>
<IconButton onClick={() => handleEditClick(todo)}>
<Edit />
</IconButton>
<IconButton onClick={() => deleteTodo(todo.id)}>
<Delete />
</IconButton>
</>
)}
style={{
display: todo.completed ? 'none' : 'flex',
opacity: todo.completed ? 0.7 : 1
}}
>
<Checkbox
checked={Boolean(todo.completed)}
onChange={() => updateTodo(todo, true)}
/>
<ListItemText
primary={todo.title}
secondary={todo.description}
style={{
textDecoration: todo.completed ? 'line-through' : 'none',
}}
/>
</ListItem>
))}
</List>
<div style={{ display: 'flex', gap: 10, marginBottom: 20 }}>
<TextField
fullWidth
label="New Todo Title"
value={newTodo.title}
onChange={(e) => setNewTodo({ ...newTodo, title: e.target.value })}
/>
<TextField
fullWidth
label="Description"
value={newTodo.description}
onChange={(e) => setNewTodo({ ...newTodo, description: e.target.value })}
/>
<Button
variant="contained"
color="primary"
onClick={createTodo}
>
Add
</Button>
</div>
<Dialog open={Boolean(editingTodo)} onClose={handleClose}>
<DialogTitle>Edit Todo</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="Title"
fullWidth
value={editForm.title}
onChange={(e) => setEditForm({ ...editForm, title: e.target.value })}
/>
<TextField
margin="dense"
label="Description"
fullWidth
multiline
rows={3}
value={editForm.description}
onChange={(e) => setEditForm({ ...editForm, description: e.target.value })}
/>
<FormControlLabel
control={(
<Checkbox
checked={editForm.completed}
onChange={(e) => setEditForm({ ...editForm, completed: e.target.checked })}
/>
)}
label="Completed"
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button
onClick={handleSave}
color="primary"
disabled={!editForm.title.trim()}
>
Save
</Button>
</DialogActions>
</Dialog>
</div>
);
- 搜索框:允许用户输入搜索词,实时搜索TODO列表。
- TODO列表:显示所有TODO项,每个项包含标题、描述、编辑和删除按钮,以及完成状态Checkbox。
- 添加TODO表单:允许用户输入新TODO的标题和描述,点击“Add”按钮创建。
- 编辑对话框:当用户点击编辑按钮时,显示编辑表单,允许修改TODO的标题、描述和完成状态。
功能实现
- 添加TODO项:用户输入标题和描述后,点击“Add”按钮,发送POST请求到后端,创建新的TODO项。
- 编辑TODO项:用户点击编辑按钮,打开编辑对话框,修改TODO项的详细信息后,点击“Save”按钮,发送PUT请求到后端,更新TODO项。
- 删除TODO项:用户点击删除按钮,发送DELETE请求到后端,删除指定的TODO项。
- 搜索TODO项:用户输入搜索词,组件会自动搜索标题或描述中包含该词的TODO项,支持防抖动功能,减少请求次数。
- 标记完成:用户点击Checkbox,TODO项会被标记为完成,样式会变为灰色并添加删除线。
优化建议
尽管这段代码已经可以正常工作,但为了提升应用的性能和用户体验,可以考虑以下优化:
- 添加加载状态:在数据加载过程中,显示加载动画,提升用户体验。
- 添加错误提示:在请求失败时,显示错误提示信息,帮助用户了解问题所在。
- 添加成功提示:在创建、更新或删除TODO项成功后,显示成功提示信息。
- 优化样式:根据Material Design规范,优化组件的样式和布局,提升视觉效果。
- 添加响应式设计:确保应用在不同设备上都能良好显示,提升应用的适应性。
- 添加数据验证:在前端和后端都添加数据验证,确保输入的数据合法有效。
总结
通过本文,我们详细解析了一个使用React和Material-UI构建的TODO列表应用。从状态管理、HTTP请求到组件渲染,每个部分都进行了详细的解释。希望这篇文章能够帮助读者理解如何使用这些技术构建一个简单的TODO应用,并为进一步的学习和开发打下基础。