一、案例要求:
1.实现组件分类,分为头部输,列表,底部
2.实现输入任务名称渲染到列表里
3.单独删除任务
4.全选与全不选
5.删除勾选任务
二、实现原理
1.完成组件创建
2.通过ref绑定输入框的值,在通过自定义方法参数传回app.js中通过setstate改变列表中渲染数组的值
3. 通过确定被点击的元素数组id值传回app.js再通过filter方法返回id值不为传回id值的新数组,实现删除功能
4.通过循环判断传回值改变state里的值从而改变勾选状态
5.创建一个通过循环判断是否被勾选再通过filter方法传回新数组的方法在把方法放到底部组件进行调用
三、代码:
App.js
/*
* @Author: wu07 [email protected]
* @Date: 2023-02-14 16:31:46
* @LastEditors: wu07 [email protected]
* @LastEditTime: 2023-02-17 09:27:23
* @FilePath: \react\day05\demo02\tode\src\App.js
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
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 = {
todos: [
{ id: '001', name: '吃饭', done: true },
{ id: '002', name: '睡觉', done: true },
{ id: '003', name: '打代码', done: false },
{ id: '004', name: '逛街', done: false }
]
};
addTodo = (e) => {
const todos = this.state.todos;
const newTodos = [e, ...todos];
this.setState({ todos: newTodos });
};
change = (e) => {
const todos = this.state.todos;
this.setState({ todos: e });
};
updateTodo = (id, done) => {
const { todos } = this.state;
const newTodos = todos.map((todoObj) => {
if (todoObj.id === id) return { ...todoObj, done };
else return todoObj;
});
this.setState({ todos: newTodos });
};
Delete = (id) => {
const todos = this.state.todos;
const newTodos = todos.filter((e) => {
return e.id !== id;
});
this.setState({ todos: newTodos });
};
delete = () => {
const newTodos =this.state.todos.filter((e)=>{
return !e.done
})
this.setState({todos:newTodos})
};
render() {
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodo={this.addTodo} Delete={this.Delete} />
<List todos={this.state.todos} updateTodo={this.updateTodo} Delete={this.Delete} />
<Footer todos={this.state.todos} change={this.change} delete={this.delete} />
</div>
</div>
);
}
}
components/Footer/index.jsx
/*
* @Author: wu07 [email protected]
* @Date: 2023-02-14 16:36:54
* @LastEditors: wu07 [email protected]
* @LastEditTime: 2023-02-17 09:20:13
* @FilePath: \react\day05\demo02\tode\src\components\Footer\index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { Component } from 'react';
import './index.css';
export default class Footer extends Component {
state = {
flag: false,
};
// (function name(params) {
// console.log(111);
// }());
allCheck = () => {
this.state.flag = !this.state.flag;
const todos = this.props.todos;
todos.map((e) => {
if (this.state.flag == true) {
e.done = true;
}
else {
e.done = false;
}
});
this.props.change(todos);
};
delete = () => {
this.props.delete();
};
render() {
//pre初始值
const doneCount = this.props.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0);
return (
<div className="todo-footer">
<label>
<input type="checkbox" checked={this.state.flag = doneCount == 4 ? true : false} onChange={this.allCheck} />
</label>
<span>
<span>已完成{doneCount}</span> / 全部{this.props.todos.length}
</span>
<button className="btn btn-danger" onClick={this.delete}>清除已完成任务</button>
</div>
);
}
}
components/Footer/index.css
/*footer*/
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
components/List/index.jsx
/*
* @Author: wu07 [email protected]
* @Date: 2023-02-14 16:37:07
* @LastEditors: wu07 [email protected]
* @LastEditTime: 2023-02-15 17:22:04
* @FilePath: \react\day05\demo02\tode\src\components\List\index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { Component } from 'react';
import './index.css';
import Item from '../Item';
export default class List extends Component {
render() {
const { todos, updateTodo, Delete } = this.props;
return (
<ul className="todo-main">
{
todos.map(todo => {
return <Item key={todo.id} {...todo} updateTodo={updateTodo} Delete={Delete} />;
})
}
</ul>
);
}
}
components/List/index.css
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
components/Item/index.jsx
/*
* @Author: wu07 [email protected]
* @Date: 2023-02-14 16:37:05
* @LastEditors: wu07 [email protected]
* @LastEditTime: 2023-02-15 17:21:07
* @FilePath: \react\day05\demo02\tode\src\components\Item\index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { Component } from 'react';
import './index.css';
export default class Item extends Component {
state = {
mouse: false
};
change = (e) => {
this.props.updateTodo(this.props.id, e.target.checked);
};
Enter = () => {
this.setState({
mouse: true
});
};
Leave = () => {
this.setState({
mouse: false
});
};
Delete = () => { this.props.Delete(this.props.id); };
render() {
return (
<div onMouseEnter={this.Enter} onMouseLeave={this.Leave}><li>
<label>
<input type="checkbox" checked={this.props.done} onChange={this.change} />
<span>{this.props.name}</span>
</label>
<button className="btn btn-danger" style={
{ display: this.state.mouse ? 'block' : 'none' }} onClick={this.Delete}>删除</button>
</li>
</div>
);
}
}
components/Item/index.css
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
components/Header/index.jxs
/*
* @Author: wu07 [email protected]
* @Date: 2023-02-14 16:37:03
* @LastEditors: wu07 [email protected]
* @LastEditTime: 2023-02-15 13:23:06
* @FilePath: \react\day05\demo02\tode\src\components\Header\index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import React, { Component } from 'react';
import './index.css';
import { nanoid } from 'nanoid';
export default class Header extends Component {
handleKeyUp = (e) => {
if (e.keyCode != 13) {
return;
}
if (e.target.value.trim() === '') {
alert("输入不能为空");
return;
}
//nanoid()方法生成唯一id标识符
const a = { id: nanoid(), name: e.target.value, done: false };
this.props.addTodo(a);
//清空输入
e.target.value = '';
};
render() {
return (
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp} />
</div>
);
}
}
components/Header/index.css
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
四、小结
这个小案例里有数据传递渲染和组件导入使用,符合初学react的朋友。