本文记录用react实现一个简单待办事项表的具体过程,包括新建、修改、删除事项。最终成品如下:
步骤
新建待办事项
修改 App.js,首先完成新建的功能:
useState是React 的 Hook,用于管理组件的状态。todos 是待办事项列表,setTodos 是更新状态的函数。
可以观察到return()里的部分是类html语句。return()是在 JavaScript(尤其是 React 中)常见的语法结构,通常用于返回 JSX 代码。这是因为React 使用 JSX 来描述界面结构,在 React 组件中,return 语句用于返回一个 UI 元素或组件的渲染内容。JSX 使得 React 的组件能够在 JavaScript 代码中直接编写类似 HTML 的标签。它被 React 转换成 JavaScript 来生成真实的 DOM 元素。
事件绑定onChange函数绑定输入框的变更事件,将用户输入同步到状态;onClick绑定按钮点击事件,执行添加待办事项的逻辑。
使用 map()方法遍历 todos 数组,生成 li 元素。每个列表项需要添加唯一的 key 属性,用于标识该元素。
export default App; 将 App 组件导出为默认导出,以便其他文件能够导入和使用它。其他文件(如 index.js)可以通过 import App from './App'; 来导入该组件。
import React, { useState } from 'react';
function App() {
const [todos, setTodos] = useState([]); // 初始化待办事项列表
const [newTodo, setNewTodo] = useState(''); // 输入框的内容
const addTodo = () => {
if (newTodo.trim() === '') return;
setTodos([...todos, { id: Date.now(), text: newTodo }]); // 添加新的待办事项
setNewTodo(''); // 清空输入框
};
return (
<div>
<h1>待办事项列表</h1>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)} // 双向绑定输入框
placeholder="input todo list"
/>
<button onClick={addTodo}>New...</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li> // 渲染待办事项列表
))}
</ul>
</div>
);
}
export default App;
分离待办事项为组件
由于会创建出很多条事项,他们的结构都是相同的,因此可以创建TodoItem.js,将每一项已有的待办事项用同一个组件表示,便于代码复用和维护。TodoItem({ todo })表示todo作为一个传递值,由引用它的父组件给出:
import React from 'react';
function TodoItem({ todo }) {
return (
<li>
{todo.text}
</li>
);
}
export default TodoItem;
然后修改App.js:
需要import以上子组件,然后通过<TodoItem />的格式使用组件。
import TodoItem from './components/TodoItem'; // 引入子组件
function App() {
...
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
添加编辑与删除功能
编辑和删除是每个已有待办事项的公共功能,因此写在TodoItem.js里:
通过 isEditing 的三目运算符切换待办项的展示模式(普通模式或编辑模式)。编辑功能的逻辑由 App 管理(父组件),为了通信通过 props 将方法传递到子组件 TodoItem。
在这部分可以看到丰富的函数调用。
- 受控组件 输入框的值由状态 editText 管理,称为受控组件。 每次用户输入内容时,onChange 事件会触发状态更新,使得输入框始终和状态同步。
- 状态管理 editText:存储输入框的内容。 isEditing:控制是否处于编辑模式。
- 事件处理 React 使用 onClick、onChange 等事件属性绑定事件处理函数。 事件处理函数中,可以通过状态更新(如 setEditText 和 setIsEditing)动态更新界面。
- 父子组件通信 onEdit(todo.id, editText) 调用了父组件传递的 onEdit 回调,将子组件的编辑结果(待办事项的 ID 和新的内容)传递给父组件,让父组件更新对应的待办事项。
import React, { useState } from 'react';
function TodoItem({ todo, onEdit, onDelete }) {
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(todo.text);
// 保存编辑
const saveEdit = () => {
if (editText.trim() === '') return;
onEdit(todo.id, editText);
setIsEditing(false);
};
return (
<li style={styles.item}>
{isEditing ? (
<div style={styles.editContainer}>
<input
type="text"
value={editText}
onChange={(e) => setEditText(e.target.value)}
style={styles.input}
/>
<button onClick={saveEdit} style={styles.saveButton}>save</button>
<button onClick={() => setIsEditing(false)} style={styles.cancelButton}>cancel</button>
</div>
) : (
<div style={styles.viewContainer}>
<span>{todo.text}</span>
<div>
<button onClick={() => setIsEditing(true)} style={styles.editButton}>edit</button>
<button onClick={() => onDelete(todo.id)} style={styles.deleteButton}>delete</button>
</div>
</div>
)}
</li>
);
}
同时在App.js里更新待办事项。filter函数的作用是去除选定的对象:
// 编辑待办事项
const editTodo = (id, newText) => {
setTodos(todos.map(todo => (todo.id === id ? { ...todo, text: newText } : todo)));
};
// 删除待办事项
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
添加样式
点击编辑后的效果,可以选择保存或取消:
最后通过const styles = {};的方式添加样式美化界面。可以观察到React 中的 style 属性与传统 CSS 不一样,它是一个 JavaScript 对象,属性值是字符串或数字(数字自动加 px)。这样的内联样式方便动态调整,但不适合管理复杂的全局样式。这种设计的目的是为了更好地与 JavaScript 生态集成,同时保留动态调整样式的灵活性。
input: {
flex: 1,
padding: '5px',
},
总结
在实现这个简单的待办事项列表应用时,我经历了从基础的状态管理到组件拆分,再到功能增强的全过程。通过这个项目,可以理解 React 中的多个核心概念,例如组件化开发、状态管理、事件处理、条件渲染和父子组件通信。
之后可以对这个项目做出更多人性化的改进:
- 完成状态:可以为每个待办事项添加一个 完成/未完成 状态,并允许点击按钮来切换状态,显示完成的待办事项和未完成的待办事项的不同样式。
- 排序功能:可以按照不同的标准对待办事项进行排序。
- 拖拽排序:实现待办事项的拖拽排序功能,自由调整事项的顺序。