文章目录
- 目标实现效果
- 实现思路
- 实现步骤
- 第一步:App 组件中定义删除 Todo 的组件方法
- 第二步:App 组件通过 props 传递删除 Todo 的方法到子组件 List
- 第三步:List 组件接收删除 Todo 的方法,并传递给 Item 子组件
- 第四步:Item 组件接收并使用 props 传入的删除 Todo 方法
- 完整代码
- App 组件完整代码
- List 组件完整代码
- Item 组件完整代码
前面我们已经实现了当鼠标悬浮在某条 Todo Item 上时,显示删除按钮,那么本文实现点击删除按钮时,从 TodoList 列表中删除一条 Todo 记录。
目标实现效果
实现思路
有了前面章节的基础和经验,实现删除功能就应该轻车熟路了。
还是通过父子组件之间通信,使用 props
从父组件把一个删除 Todo 的方法传递到子组件中,然后子组件通过调用父组件传递进来的删除方法,并把相关数据通过参数传递给父组件。
实现步骤
第一步:App 组件中定义删除 Todo 的组件方法
在 App 组件中定义一个 deleteTodo
的组件方法,用来删除一条 Todo 记录,代码片段如下:
// src/App.js
// 用于删除一条 Todo
deleteTodo = (id) => {
// 获取原来的 todoList
const { todoList } = this.state;
// 删除指定 id 的 todo 对象
const newTodoList = todoList.filter((todoObj) => {
return todoObj.id !== id;
});
// 更新状态
this.setState({ todoList: newTodoList });
};
代码解析: 通过数据 ES6 高级数据函数
filter
实现从 TodoList 数组中删除指定的元素。
第二步:App 组件通过 props 传递删除 Todo 的方法到子组件 List
通过 props
将 deleteTodo
传递到 List 组件中,代码片段如下:
// src/App.js
<List
todoList={todoList}
updateTodo={this.updateTodo}
deleteTodo={this.deleteTodo}
/>
第三步:List 组件接收删除 Todo 的方法,并传递给 Item 子组件
List 组件通过 props
将接收到的 deleteTodo
方法传递到 Item 组件中
代码片段如下:
// src/components/List/index.jsx
// 从 props 接收删除 Todo 的方法 deleteTodo
const { todoList, updateTodo, deleteTodo } = this.props;
// 将 deleteTodo 方法传递给 Item 组件
<Item
key={todo.id}
{...todo}
updateTodo={updateTodo}
deleteTodo={deleteTodo}
/>;
第四步:Item 组件接收并使用 props 传入的删除 Todo 方法
在 Item 组件中,在删除按钮 button 的 onClick
事件的回调方法内调用 props
传进来的 deleteTodo
方法并将要删除的 Todo 的记录 id 值作为参数传递到 App 组件中,
App 组件接收参数,并更新状态 state
,从而使得 List 数据列表的更新。
代码片段如下:
// src/components/Item/index.jsx
// 给 button 绑定 onClick 事件
<button
onClick={() => this.handleDelete(id)}
className="btn btn-danger btn-sm"
style={{ display: mouse ? "block" : "none" }}
>
删除
</button>;
// 点击删除按钮的回到
handleDelete = (id) => {
if (window.confirm("确定删除吗?")) {
this.props.deleteTodo(id);
}
};
注意: confirm 前需要添加 window,否则会报错。
至此,完成了从 TodoList 列表中删除一条 Todo。
完整代码
App 组件完整代码
// file: src/App.js
// 创建“外壳”组件
import React, { Component } from "react";
import Header from "./components/Header";
import List from "./components/List";
import Footer from "./components/Footer";
import "./App.css";
export default class App extends Component {
// 总结:状态在哪里,操作状态的方法就在哪里。
// 初始化状态
state = {
todoList: [
{ id: 1, name: "参加晨会", done: true },
{ id: 2, name: "A功能开发", done: true },
{ id: 3, name: "B功能开发", done: false },
],
};
/**
* addTodo 用于添加一条 Todo 记录,接收的参数是 Todo 对象
*/
addTodo = (todoObj) => {
// 获取原 TodoList
const { todoList } = this.state;
// 追加一条 Todo
const newTodoList = [todoObj, ...todoList];
// 更新状态
this.setState({ todoList: newTodoList });
};
// 用于更新一个 Todo 对象
updateTodo = (id, done) => {
// 获取状态的中 todoList
const { todoList } = this.state;
// 匹配处理数据
const newTodoList = todoList.map((todoObj) => {
if (todoObj.id === id) return { ...todoObj, done };
else return todoObj;
});
// 更新状态
this.setState({ todoList: newTodoList });
};
// 用于删除一条 Todo
deleteTodo = (id) => {
// 获取原来的 todoList
const { todoList } = this.state;
// 删除指定 id 的 todo 对象
const newTodoList = todoList.filter((todoObj) => {
return todoObj.id !== id;
});
// 更新状态
this.setState({ todoList: newTodoList });
};
render() {
const { todoList } = this.state;
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodo={this.addTodo} />
<List
todoList={todoList}
updateTodo={this.updateTodo}
deleteTodo={this.deleteTodo}
/>
<Footer />
</div>
</div>
);
}
}
List 组件完整代码
// file: src/components/List/index.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import Item from "../Item";
import "./index.css";
export default class List extends Component {
// 对接收的 props 进行:类型、必要性的限制
static propTypes = {
todoList: PropTypes.array.isRequired,
updateTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
};
render() {
const { todoList, updateTodo, deleteTodo } = this.props;
return (
<ul className="todo-main">
{todoList.map((todo) => {
return (
<Item
key={todo.id}
{...todo}
updateTodo={updateTodo}
deleteTodo={deleteTodo}
/>
);
})}
</ul>
);
}
}
Item 组件完整代码
// file: src/components/Item/index.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
import "./index.css";
export default class Item extends Component {
// 对接收的 props 进行:类型、必要性的限制
static propTypes = {
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
done: PropTypes.bool.isRequired,
updateTodo: PropTypes.func.isRequired,
deleteTodo: PropTypes.func.isRequired,
};
state = { mouse: false }; //标识鼠标移入、移出
// 鼠标移入、移出的回调
handleMouse = (flag) => {
return () => {
this.setState({ mouse: flag });
};
};
// 勾选、取消勾选某一个 Todo 的回调
handleCheck = (id) => {
return (event) => {
this.props.updateTodo(id, event.target.checked);
};
};
// 点击删除按钮的回到
handleDelete = (id) => {
if (window.confirm("确定删除吗?")) {
this.props.deleteTodo(id);
}
};
render() {
const { id, name, done } = this.props;
const { mouse } = this.state;
return (
<li
style={{ backgroundColor: mouse ? "#eee" : "#fff" }}
onMouseEnter={this.handleMouse(true)}
onMouseLeave={this.handleMouse(false)}
>
<label className="container">
<input
type="checkbox"
defaultChecked={done}
onChange={this.handleCheck(id)}
/>
<span>{name}</span>
</label>
<button
onClick={() => this.handleDelete(id)}
className="btn btn-danger btn-sm"
style={{ display: mouse ? "block" : "none" }}
>
删除
</button>
</li>
);
}
}