使用React实现一个简单的待办事项列表|青训营
什么是待办事项列表?
待办事项列表(To-do List)是一种用于管理个人或团队的任务和目标的工具,它可以帮助我们记录、安排、执行和跟踪我们想要完成的事情。
一个典型的待办事项列表应用程序通常具有以下功能:
- 添加一个新的待办事项
- 编辑一个已有的待办事项
- 删除一个不需要的待办事项
- 标记一个已完成的待办事项
- 筛选不同状态的待办事项
为什么使用React来实现待办事项列表?
React是一个用于构建用户界面的JavaScript库,它可以让我们用组件的方式来开发网页或应用程序,提高了代码的可读性和可维护性。
React还有以下优点:
- 使用虚拟DOM(Virtual DOM)来提高渲染性能,只更新变化的部分,避免不必要的重绘。
- 使用JSX语法来编写组件,让我们可以在JavaScript中直接使用HTML标签,简化了视图的创建和渲染过程。
- 使用Hooks来在函数组件中使用状态和生命周期,让我们可以不用类组件就能实现复杂的逻辑和交互。
- 使用Props和State来管理数据流,让我们可以实现数据和视图的单向绑定,保证数据的一致性和可控性。
如何使用React来实现待办事项列表?
为了使用React来实现待办事项列表,我们需要完成以下几个步骤:
- 创建一个React项目
- 设计组件结构
- 编写组件代码
- 添加样式和交互
下面我们将逐一介绍这些步骤。
创建一个React项目
要创建一个React项目,我们可以使用create-react-app工具,它是一个官方推荐的方式,可以帮助我们快速搭建一个React开发环境,包括了Webpack、Babel、ESLint等常用的工具和配置。
要使用create-react-app工具,我们需要先安装Node.js和npm(或yarn),然后在命令行中执行以下命令:
# 使用npm
npx create-react-app todo-list
# 使用yarn
yarn create react-app todo-list
这样就会在当前目录下创建一个名为todo-list的文件夹,并且在其中生成了一些基本的文件和目录结构。
接下来,我们可以进入todo-list目录,并启动开发服务器:
# 进入todo-list目录
cd todo-list
# 使用npm
npm start
# 使用yarn
yarn start
这样就会在浏览器中打开一个地址为http://localhost:3000/ 的页面,显示了一个默认的React应用界面。
设计组件结构
在React中,组件是构建用户界面的基本单位,它可以让我们将复杂的界面分解为多个独立且可复用的部分。
为了设计我们的待办事项列表应用程序的组件结构,我们可以先从用户界面出发,分析出其中包含了哪些组件,并且它们之间有什么关系。
我们可以参考以下示意图来设计我们的组件结构:
![component-structure]
从图中可以看出,我们的应用程序由以下几个组件组成:
- App:根组件,负责管理整个应用程序的状态和逻辑,以及渲染其他组件。
- TodoForm:表单组件,负责接收用户输入,以及添加新的待办事项。
- TodoList:列表组件,负责展示所有的待办事项,以及传递相关的事件和数据给子组件。
- TodoItem:项目组件,负责展示一个单独的待办事项,以及处理相关的事件和数据。
- TodoFilter:过滤器组件,负责提供不同的筛选条件,以及切换当前的筛选状态。
我们可以使用以下代码来创建这些组件,并且在App组件中引入和渲染它们:
// App.js
import React from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
function App() {
return (
<div className="todoapp">
<h1>Todo List</h1>
<TodoForm />
<TodoFilter />
<TodoList />
</div>
);
}
export default App;
// TodoForm.js
import React from "react";
function TodoForm() {
return (
<form>
<input type="text" placeholder="Enter a new task" />
<button type="submit">Add</button>
</form>
);
}
export default TodoForm;
// TodoList.js
import React from "react";
import TodoItem from "./TodoItem";
function TodoList() {
return (
<ul>
<TodoItem />
<TodoItem />
<TodoItem />
</ul>
);
}
export default TodoList;
// TodoItem.js
import React from "react";
function TodoItem() {
return (
<li>
<input type="checkbox" />
<span>Task</span>
<button>Edit</button>
<button>Delete</button>
</li>
);
}
export default TodoItem;
// TodoFilter.js
import React from "react";
function TodoFilter() {
return (
<div>
<button>All</button>
<button>Active</button>
<button>Completed</button>
</div>
);
}
export default TodoFilter;
现在,我们可以看到我们的应用程序已经有了一个基本的界面,如下图所示:
![basic-ui]
编写组件代码
在设计了组件结构之后,我们需要编写每个组件的具体代码,实现它们的功能和交互。
为了编写组件代码,我们需要使用React提供的一些特性和技术,包括:
- Props:一种用于传递数据和方法给子组件的机制,它可以让我们实现组件之间的通信和协作。
- State:一种用于存储和更新组件内部的状态的机制,它可以让我们实现组件的动态变化和响应。
- Hooks:一种用于在函数组件中使用状态和生命周期等特性的机制,它可以让我们实现更简洁和优雅的组件代码。
- JSX:一种用于在JavaScript中编写HTML标签的语法,它可以让我们更方便地创建和渲染组件的视图。
下面我们将逐一介绍这些特性和技术,并且使用它们来编写我们的组件代码。
Props
Props是一种用于传递数据和方法给子组件的机制,它可以让我们实现组件之间的通信和协作。
在React中,每个组件都可以接收一个props参数,它是一个JavaScript对象,包含了父组件传递给子组件的属性和值。
例如,在App组件中,我们可以给TodoList组件传递一个名为tasks的props属性,它是一个数组,包含了所有的待办事项:
// App.js
import React from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
function App() {
// 定
Props
Props是一种用于传递数据和方法给子组件的机制,它可以让我们实现组件之间的通信和协作。
在React中,每个组件都可以接收一个props参数,它是一个JavaScript对象,包含了父组件传递给子组件的属性和值。
例如,在App组件中,我们可以给TodoList组件传递一个名为tasks的props属性,它是一个数组,包含了所有的待办事项:
// App.js
import React from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
function App() {
// 定义一个tasks数组,存储所有的待办事项
const tasks = [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
];
return (
<div className="todoapp">
<h1>Todo List</h1>
<TodoForm />
<TodoFilter />
{/* 给TodoList组件传递tasks属性 */}
<TodoList tasks={tasks} />
</div>
);
}
export default App;
然后,在TodoList组件中,我们可以通过props参数来接收并使用tasks属性,例如,我们可以遍历tasks数组,为每个待办事项创建一个TodoItem组件,并且给它传递一些props属性,如id、text和completed等:
// TodoList.js
import React from "react";
import TodoItem from "./TodoItem";
function TodoList(props) {
// 从props中解构出tasks属性
const { tasks } = props;
return (
<ul>
{/* 遍历tasks数组,为每个待办事项创建一个TodoItem组件 */}
{tasks.map((task) => (
// 给TodoItem组件传递id、text和completed等属性
<TodoItem key={task.id} id={task.id} text={task.text} completed={task.completed} />
))}
</ul>
);
}
export default TodoList;
同样地,在TodoItem组件中,我们也可以通过props参数来接收并使用id、text和completed等属性,例如,我们可以根据这些属性来渲染不同的视图元素,并且添加一些事件处理函数:
// TodoItem.js
import React from "react";
function TodoItem(props) {
// 从props中解构出id、text和completed等属性
const { id, text, completed } = props;
// 定义一些事件处理函数
const handleCheck = () => {
console.log(`Check task ${id}`);
};
const handleEdit = () => {
console.log(`Edit task ${id}`);
};
const handleDelete = () => {
console.log(`Delete task ${id}`);
};
return (
<li>
{/* 根据completed属性来设置复选框的状态 */}
<input type="checkbox" checked={completed} onChange={handleCheck} />
{/* 根据completed属性来设置文本的样式 */}
<span style={{ textDecoration: completed ? "line-through" : "none" }}>{text}</span>
<button onClick={handleEdit}>Edit</button>
<button onClick={handleDelete}>Delete</button>
</li>
);
}
export default TodoItem;
通过使用Props,我们可以实现父子组件之间的数据传递和方法调用,让我们的组件更加灵活和可复用。
State
State是一种用于存储和更新组件内部的状态的机制,它可以让我们实现组件的动态变化和响应。
在React中,每个类组件都有一个state属性,它是一个JavaScript对象,包含了该组件内部的状态。我们可以通过this.state来访问该对象,并且通过this.setState来更新该对象。
例如,在App组件中,我们可以定义一个state对象,包含了一个名为tasks的状态,它是一个数组,包含了所有的待办事项:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
render() {
return (
<div className="todoapp">
<h1>Todo List</h1>
<TodoForm />
<TodoFilter />
{/* 给TodoList组件传递tasks属性 */}
<TodoList tasks={this.state.tasks} />
</div>
);
}
}
export default App;
然后,在App组件中,我们可以定义一些方法来更新tasks状态,例如,我们可以定义一个addTask方法,用来添加一个新的待办事项,并且通过this.setState来更新tasks状态:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
// 定义一个addTask方法,用来添加一个新的待办事项
addTask = (text) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 创建一个新的待办事项对象,id为当前tasks长度加一,text为参数,completed为false
const newTask = { id: tasks.length + 1, text, completed: false };
// 使用this.setState来更新tasks状态,将新的待办事项添加到tasks数组的末尾
this.setState({
tasks: [...tasks, newTask],
});
};
render() {
return (
<div className="todoapp">
<h1>Todo List</h1>
{/* 给TodoForm组件传递addTask方法 */}
<TodoForm addTask={this.addTask} />
<TodoFilter />
{/* 给TodoList组件传递tasks属性 */}
<TodoList tasks={this.state.tasks} />
</div>
);
}
}
export default App;
同样地,在App组件中,我们也可以定义其他的方法来更新tasks状态,例如,我们可以定义一个toggleTask方法,用来切换一个待办事项的完成状态,并且通过this.setState来更新tasks状态:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
// 定义一个addTask方法,用来添加一个新的待办事项
addTask = (text) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 创建一个新的待办事项对象,id为当前tasks长度加一,text为参数,completed为false
const newTask = { id: tasks.length + 1, text, completed: false };
// 使用this.setState来更新tasks状态,将新的待办事项添加到tasks数组的末尾
this.setState({
tasks: [...tasks, newTask],
});
};
// 定义一个toggleTask方法,用来切换一个待办事项的完成状态
toggleTask = (id) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 使用map方法遍历tasks
State
State是一种用于存储和更新组件内部的状态的机制,它可以让我们实现组件的动态变化和响应。
在React中,每个类组件都有一个state属性,它是一个JavaScript对象,包含了该组件内部的状态。我们可以通过this.state来访问该对象,并且通过this.setState来更新该对象。
例如,在App组件中,我们可以定义一个state对象,包含了一个名为tasks的状态,它是一个数组,包含了所有的待办事项:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
render() {
return (
<div className="todoapp">
<h1>Todo List</h1>
<TodoForm />
<TodoFilter />
{/* 给TodoList组件传递tasks属性 */}
<TodoList tasks={this.state.tasks} />
</div>
);
}
}
export default App;
然后,在App组件中,我们可以定义一些方法来更新tasks状态,例如,我们可以定义一个addTask方法,用来添加一个新的待办事项,并且通过this.setState来更新tasks状态:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
// 定义一个addTask方法,用来添加一个新的待办事项
addTask = (text) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 创建一个新的待办事项对象,id为当前tasks长度加一,text为参数,completed为false
const newTask = { id: tasks.length + 1, text, completed: false };
// 使用this.setState来更新tasks状态,将新的待办事项添加到tasks数组的末尾
this.setState({
tasks: [...tasks, newTask],
});
};
render() {
return (
<div className="todoapp">
<h1>Todo List</h1>
{/* 给TodoForm组件传递addTask方法 */}
<TodoForm addTask={this.addTask} />
<TodoFilter />
{/* 给TodoList组件传递tasks属性 */}
<TodoList tasks={this.state.tasks} />
</div>
);
}
}
export default App;
同样地,在App组件中,我们也可以定义其他的方法来更新tasks状态,例如,我们可以定义一个toggleTask方法,用来切换一个待办事项的完成状态,并且通过this.setState来更新tasks状态:
// App.js
import React, { Component } from "react";
import TodoForm from "./TodoForm";
import TodoList from "./TodoList";
import TodoFilter from "./TodoFilter";
class App extends Component {
// 定义一个state对象,包含了一个tasks状态
state = {
tasks: [
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build a todo app", completed: false },
{ id: 3, text: "Have fun", completed: true },
],
};
// 定义一个addTask方法,用来添加一个新的待办事项
addTask = (text) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 创建一个新的待办事项对象,id为当前tasks长度加一,text为参数,completed为false
const newTask = { id: tasks.length + 1, text, completed: false };
// 使用this.setState来更新tasks状态,将新的待办事项添加到tasks数组的末尾
this.setState({
tasks: [...tasks, newTask],
});
};
// 定义一个toggleTask方法,用来切换一个待办事项的完成状态
toggleTask = (id) => {
// 获取当前的tasks状态
const tasks = this.state.tasks;
// 使用map方法遍历tasks数组,找到与参数id相同的待办事项,然后将其completed属性取反,返回一个新的数组
const updatedTasks = tasks.map((task) => {
if (task.id === id) {
return { ...task, completed: !task.completed };
} else {
return task;
}
});
// 使用this.setState来更新tasks状态,将新的数组赋值给tasks
this.setState({
tasks: updatedTasks,
});
};
render() {
return (
<div className="todoapp">
<h1>Todo List</h1>
{/* 给TodoForm组件传递addTask方法 */}
<TodoForm addTask={this.addTask} />
<TodoFilter />
{/* 给TodoList组件传递tasks属性和toggleTask方法 */}
<TodoList tasks={this.state.tasks} toggleTask={this.toggleTask} />
</div>
);
}
}
export default App;
通过使用State,我们可以实现组件内部的状态管理和更新,让我们的组件更加动态和响应。