ThatManK Mobile Article
todoList_2-逻辑实现
逻辑实现
一、渲染 todo 列表
- App.js 初始化状态, 把状态传给 List
export default class App extends Component {
//初始化状态
state = {
todos: [
{ id: "001", name: "吃饭", done: true },
{ id: "002", name: "睡觉", done: true },
{ id: "003", name: "打代码", done: false },
{ id: "004", name: "逛街", done: false },
],
};
render() {
const { todos } = this.state;
...
<List todos={todos} />
}
}
- List 接受传参, 并传递给 Item
import PropTypes from "prop-types";
export default class List extends Component {
//对接收的props进行:类型、必要性的限制
static propTypes = {
todos: PropTypes.array.isRequired,
};
render() {
const { todos } = this.props;
return (
<div className="list">
{todos.map((todo) => {
return <Item key={todo.id} {...todo} />;
})}
</div>
);
}
}
- Item 渲染数据
export default class Item extends Component {
render() {
const { id, name, done } = this.props;
return (
<>
<li>
<label style={{ display: "flex" }}>
<input
type="checkbox"
style={{ verticalAlign: "middle", marginTop: 0 }}
/>
<span>{name}</span>
</label>
<button style={{ display: "none" }}>删除</button>
</li>
</>
);
}
}
- 如图

二、实现添加 todo 对象
- App.js 将添加 todo 的方法传递给 Header, 并处理 Header 添加的 todo 对象
export default class App extends Component {
// addTodo用于添加一个todo, 接收的参数是todo对象
addTodo = (todoObj) => {
// 获取原todos
const { todos } = this.state;
// 追加一个todo
const newTodos = [todoObj, ...todos];
// 更新状态
this.setState({ todos: newTodos });
};
render() {
...
<Header addTodo={this.addTodo} />;
}
}
- Header 接受方法参数添加业务逻辑,并传回要添加的 todo 对象
import PropTypes from "prop-types";
// 生成随机ID, 需要提前 `npm add nanoid`
import { nanoid } from "nanoid";
export default class Header extends Component {
// 对接收的props进行:类型、必要性的限制
static propTypes = {
addTodo: PropTypes.func.isRequired,
};
// 键盘事件的回调
handleKeyUp = (event) => {
// 解构赋值获取keyCode,target
const { keyCode, target } = event;
// 判断是否是回车按键
if (keyCode !== 13) return;
// 添加的todo名字不能为空
if (target.value.trim() === "") {
alert("输入不能为空");
return;
}
// 准备好一个todo对象
const todoObj = { id: nanoid(), name: target.value, done: false };
// 将todoObj传递给App
this.props.addTodo(todoObj);
// 清空输入
target.value = "";
};
render() {
return (
<>
<input
className="header-input"
onKeyUp={this.handleKeyUp}
type="text"
placeholder="请输入任务名称, 摁回车键确认"
/>
</>
);
}
}
三、实现删除 tudo 对象
- App.js 添加删除 todo 方法, 传递给 List, 并处理 List 回传的 id,根据 id 删除
export default class App extends Component {
//deleteTodo用于删除一个todo对象
deleteTodo = (id) => {
//获取原来的todos
const { todos } = this.state;
//删除指定id的todo对象
const newTodos = todos.filter((todoObj) => {
return todoObj.id !== id;
});
//更新状态
this.setState({ todos: newTodos });
};
render() {
return (
...
<List todos={todos} deleteTodo={this.deleteTodo} />
);
}
}
- List 接受传参, 并直接发送给 Item
export default class List extends Component {
//对接收的props进行:类型、必要性的限制
static propTypes = {
...
deleteTodo: PropTypes.func.isRequired,
};
render() {
const { todos, deleteTodo } = this.props;
return (
<div className="list">
{todos.map((todo) => {
return <Item key={todo.id} {...todo} deleteTodo={deleteTodo} />;
})}
</div>
);
}
}
- Item 处理业务逻辑, 并回传任务 id
export default class Item extends Component {
state = { mouse: false }; // 标识鼠标移入、移出
// 鼠标移入、移出的回调
handleMouse = (flag) => {
return () => {
this.setState({ mouse: flag });
};
};
// 删除一个todo的回调
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 ? "#ddd" : "white" }}
onMouseEnter={this.handleMouse(true)}
onMouseLeave={this.handleMouse(false)}
>
<label style={{ display: "flex" }}>
<input
type="checkbox"
style={{ verticalAlign: "middle", marginTop: 0 }}
/>
<span>{name}</span>
</label>
<button
style={{ display: mouse ? "block" : "none" }}
onClick={() => this.handleDelete(id)}
>
删除
</button>
</li>
</>
);
}
}
四、实现 checkbox 更新 tudo 对象
- App.js 实现更新方法, 并传递给 List
export default class App extends Component {
// updateTodo用于更新一个todo对象
updateTodo = (id, done) => {
// 获取状态中的todos
const { todos } = this.state;
// 匹配处理数据
const newTodos = todos.map((todoObj) => {
if (todoObj.id === id) return { ...todoObj, done };
else return todoObj;
});
this.setState({ todos: newTodos });
};
render() {
const { todos } = this.state;
return (
...
<List
todos={todos}
deleteTodo={this.deleteTodo}
updateTodo={this.updateTodo}
/>
);
}
}
- List 传递给 Item 处理
export default class List extends Component {
static propTypes = {
...
updateTodo: PropTypes.func.isRequired,
};
render() {
const { todos, deleteTodo, updateTodo } = this.props;
return (
...
<Item
key={todo.id}
{...todo}
deleteTodo={deleteTodo}
updateTodo={updateTodo}
/>
);
}
}
- Item 处理并回传 item 的 id 和选中的状态
export default class Item extends Component {
// 勾选、取消勾选某一个todo的回调
handleCheck = (id) => {
return (event) => {
this.props.updateTodo(id, event.target.checked);
};
};
render() {
const { id, name, done } = this.props;
const { mouse } = this.state;
return (
...
<input
type="checkbox"
style={{ verticalAlign: "middle", marginTop: 0 }}
checked={done}
onChange={this.handleCheck(id)}
/>
);
}
}
五、实现 Footer 对 todo 全选
- App.js 将 todo 列表传给 Footer
export default class App extends Component {
render() {
const { todos } = this.state;
return (
...
<Footer todos={todos}/>
);
}
}
- Footer 实时渲染
export default class Footer extends Component {
render() {
const { todos } = this.props;
// 已完成的个数
const doneCount = todos.reduce(
(pre, todo) => pre + (todo.done ? 1 : 0),
0
);
// 总数
const total = todos.length;
return (
<div className="footer">
<label>
<input
type="checkbox"
style={{ verticalAlign: "middle", marginTop: 0 }}
checked={doneCount === total && total !== 0 ? true : false}
/>
</label>
<span>
<span>已完成{doneCount}</span> / 全部{total}
</span>
<button>清除已完成任务</button>
</div>
);
}
}
- App.js 将全部选定/取消的方法传给 Footer
export default class App extends Component {
// checkAllTodo用于全选
checkAllTodo = (done) => {
// 获取原来的todos
const { todos } = this.state;
// 加工数据
const newTodos = todos.map((todoObj) => {
return { ...todoObj, done };
});
// 更新状态
this.setState({ todos: newTodos });
};
render() {
...
return (
...
<Footer todos={todos} checkAllTodo={this.checkAllTodo} />
);
}
}
- Footer 实现逻辑, 并回传
export default class Footer extends Component {
// 全选checkbox的回调
handleCheckAll = (event) => {
this.props.checkAllTodo(event.target.checked);
};
render() {
...
<input
type="checkbox"
style={{ verticalAlign: "middle", marginTop: 0 }}
checked={doneCount === total && total !== 0 ? true : false}
onChange={this.handleCheckAll}
/>;
}
}
六、实现 Footer 对 todo 选中删除
- App.js 删除选中的方法
export default class App extends Component {
// clearAllDone用于清除所有已完成的
clearAllDone = () => {
// 获取原来的todos
const { todos } = this.state;
// 过滤数据
const newTodos = todos.filter((todoObj) => {
return !todoObj.done;
});
// 更新状态
this.setState({ todos: newTodos });
};
render() {
<Footer
todos={todos}
checkAllTodo={this.checkAllTodo}
clearAllDone={this.clearAllDone}
/>;
}
}
- Footer 触发方法即可
export default class Footer extends Component {
// 清除已完成任务的回调
handleClearAllDone = () => {
this.props.clearAllDone();
};
render() {
...
<button onClick={this.handleClearAllDone}>清除已完成任务</button>;
}
}