二、TodoList案例
1. todoList案例相关知识点
-
拆分组件、实现静态组件,注意: className、 style的写法
-
动态初始化列表,如何确定将数据放在哪个组件的state中?
- 某个组件使用:放在其自身的state中
- 某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
-
关于父子之间通信:
- 【父组件】给【子组件】传递数据:通过props传递
- 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
- 注意defaultchecked 和 checked的区别,类似的还有: defaultvalue和value
- 状态在哪里,操作状态的方法就在哪里
2. 代码:
-
App.jsx
export default class App extends Component { // 初始化状态 state = { todos: [ { id: "001", name: "吃饭", done: true }, { id: "002", name: "睡觉", done: true }, { id: "003", name: "敲代码", done: false }, ], }; // 添加一个todo addTodo = (todoObj) => { this.setState({ todos: [todoObj, ...this.state.todos], }); }; // 修改todo的勾选 updateTodo = (id, state) => { this.setState({ todos: this.state.todos.map((todo) => { // 方式一 // if (todo.id === id) todo.done = state; // return todo; //方式二 if (todo.id === id) return { ...todo, done: state }; return todo; }), }); }; // 删除一个todo deleteTodo = (id) => { this.setState({ todos: this.state.todos.filter((x) => x.id !== id), }); }; // 反选 checkAllTodo = (done) => { this.setState({ todos: this.state.todos.map((todo) => { return { ...todo, done }; }), }); }; // 清除所有已完成 clearDone = () => { this.setState({ todos: this.state.todos.filter((x) => !x.done), }); }; render() { const { todos } = this.state; return ( <div className="todo-container"> <div className="todo-wrap"> <Header addTodo={this.addTodo} /> <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} /> <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearDone={this.clearDone} /> </div> </div> ); } }
-
List.jsx
render() { const { todos, updateTodo, deleteTodo } = this.props; return ( <ul className="todo-main"> {todos.map((todo) => { return ( <Item {...todo} key={todo.id} updateTodo={updateTodo} deleteTodo={deleteTodo} /> ); })} </ul> ); }
-
Item.jsx
export default class Item extends Component { state = { mouse: false, //鼠标移除移入的标识 }; // 鼠标移出移入的回调 handleMouse = (flag) => { return (event) => { this.setState({ mouse: flag, }); }; }; // 勾选、取消勾选的回调 handleCheck = (id) => { return (event) => { this.props.updateTodo(id, event.target.checked); }; }; // 删除一个todo handleDelete(id) { if (window.confirm("确定删除吗?")) { this.props.deleteTodo(id); } } render() { const { id, name, done } = this.props; return ( <li onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)} style={{ backgroundColor: this.state.mouse ? "#ddd" : "white" }} > <label> <input type="checkbox" checked={done} onChange={this.handleCheck(id)} /> <span>{name}</span> </label> <button className="btn btn-danger" onClick={() => this.handleDelete(id)} style={{ display: this.state.mouse ? "block" : "none" }} > 删除 </button> </li> ); } }
-
Header.jsx
export default class Header extends Component { // 对接收的props进行:类型、必要性的限制 static propTypes = { addTodo: PropTypes.func.isRequired, }; // 处理按键 handleKeyUp = (event) => { // 解构赋值 const { addTodo } = this.props; const { key, target } = event; // 判断是否按下回车 if (key !== "Enter") return; if (target.value.trim() === "") return alert("输入不能为空!"); addTodo({ id: nanoid(), name: target.value, done: false }); target.value = ""; }; render() { return ( <div className="todo-header"> <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" /> </div> ); } }
-
Footer.jsx
export default class Footer extends Component { // 全选 handleCheckAll = (event) => { this.props.checkAllTodo(event.target.checked); }; // 清除已完成 handleClearDone = () => { this.props.clearDone(); }; render() { const { todos } = this.props; // 计算已完成个数 let doneCount = todos.reduce((pre, todo) => { return todo.done ? pre + 1 : pre; }, 0); // 总数 let total = todos.reduce((pre) => { return pre + 1; }, 0); return ( <div className="todo-footer"> <label> <input type="checkbox" checked={doneCount === total && total !== 0} onChange={(event) => this.handleCheckAll(event)} /> </label> <span> <span>已完成{doneCount}</span> / 全部{total} </span> <button onClick={this.handleClearDone} className="btn btn-danger"> 清除已完成任务 </button> </div> ); } }