项目结构
|- app.js
|- app.css
|-
|components
|- Foot.js
|- Head.js
|- Item.js
|- List.js
效果图

image.png
app.js
import React, { Component } from 'react';
import './App.css';
import Foot from './components/Foot'
import Head from './components/Head'
import List from './components/List'
class App extends Component {
constructor(props) {
super(props)
this.state = {
todos: [
{ id: 1, title: '看1小时电影', finished: false },
{ id: 2, title: '看2小时电影', finished: false },
{ id: 3, title: '看3小时电影', finished: false },
{ id: 4, title: '看4小时电影', finished: false },
{ id: 5, title: '看5小时电影', finished: false },
{ id: 6, title: '看6小时电影', finished: false }
],
finishedCount: 0
}
}
// 修改完成状态
_changeTodoFinished = (todoId, isFinished) => {
const tempTodos = this.state.todos;
let finishedCount = 0;
// 1、遍历
tempTodos.forEach((todo, index) => {
if (todoId === todo.id) {
todo.finished = isFinished;
}
if (todo.finished) {
finishedCount += 1;
}
})
this.setState({
todos: tempTodos,
finishedCount
})
}
// 删除一条记录
_removeTodoWithId = (todoId) => {
const tempTodos = this.state.todos;
let finishedCount = 0;
// 1、遍历
tempTodos.forEach((todo, index) => {
if (todoId === todo.id) {
tempTodos.splice(index, 1)
}
})
// 2、处理选中的
tempTodos.forEach((todo, index) => {
if (todo.finished) {
finishedCount += 1;
}
})
// 更新状态
this.setState({
todos: tempTodos,
finishedCount
})
}
// 添加一条记录
_addOneTodo = (todo) => {
let tempTodos = this.state.todos;
tempTodos.push(todo);
this.setState({
todos: tempTodos
})
}
// 删除已完成的任务
_delCheckedTodo = () => {
let tempTodos = this.state.todos;
let tempArr = [];
tempTodos.forEach((todo) => {
if (!todo.finished) {
tempArr.push(todo);
}
})
this.setState({
todos: tempArr,
finishedCount: 0
})
}
// 选中/取消所有
_delSelectedAllTodo =(flag) => {
const tempTodos = this.state.todos;
let finishedCount = 0;
tempTodos.forEach((todo, index) => {
todo.finished = flag;
})
tempTodos.forEach((todo, index) => {
if (todo.finished) {
finishedCount += 1;
}
})
this.setState({
todos: tempTodos,
finishedCount
})
}
render() {
const { todos, finishedCount } = this.state;
return (
<div className="App" >
{ /* 头部 */}
<Head
lastTodoId={todos.length === 0 ? 0 : todos[todos.length - 1].id}
addOneTodo={this._addOneTodo}
/>
{ /* 列表 */}
<List
todos={todos}
removeTodoWithId={this._removeTodoWithId}
changeTodoFinished={this._changeTodoFinished}
/>
{ /* 底部 */}
<Foot
finishedCount={finishedCount}
totalCount={todos.length}
delCheckedTodo={this._delCheckedTodo}
delSelectedAllTodo={this._delSelectedAllTodo}
/>
</div>
)
}
}
export default App;
app.css
.App {
width: 400px;
padding: 10px;
margin: 10px auto;
border: 1px solid #ccc;
}
.todos-item {
display: flex
}
.todos-item-title {
display: inline-block;
flex: 1;
}
Foot.js
import React, { Component } from 'react'
import PropTypes from 'prop-types';
export default class Foot extends Component {
static propTypes = {
finishedCount: PropTypes.number.isRequired, // 完成数量
totalCount: PropTypes.number.isRequired, // 总数量
delCheckedTodo: PropTypes.func.isRequired, // 删除已经完成的任务
delSelectedAllTodo: PropTypes.func.isRequired // 选中/取消所有
}
render() {
const { finishedCount, totalCount, delCheckedTodo, delSelectedAllTodo } = this.props;
return (
<div style={{ paddingLeft: "40px" }}>
<label>
<input type="checkbox"
checked={finishedCount === totalCount}
onChange={() => delSelectedAllTodo(finishedCount !== totalCount)} />
<span>已完成{finishedCount}件</span>/
<span>总计{totalCount}件</span>
</label>
<button
style={{ float: 'right' }}
onClick={() => delCheckedTodo()}>清空已完成任务</button>
</div>
)
}
}
Head.js
import React, { Component } from 'react'
import PropTypes from 'prop-types';
export default class Head extends Component {
constructor(props) {
super(props)
// 绑定ref
this.myInput = React.createRef();
}
static propTypes = {
lastTodoId: PropTypes.number.isRequired, // 最后一条记录的Id
addOneTodo: PropTypes.func.isRequired // 添加一条记录
}
render() {
return (
<div>
<input ref={this.myInput}
placeholder="请输入今天的任务清单,按回车键保存"
style={{ width: '100%' }}
onKeyDown={(e) => this._handleEvent(e)}
/>
</div>
)
}
_handleEvent = (e) => {
// console.log(e.keyCode);
const { lastTodoId, addOneTodo } = this.props;
// 判断是否是回车键
if (13 === e.keyCode) {
// 判断输入的内容是否为空
if (!this.myInput.current.value.trim()) {
alert("输入的内容不能为空!");
return;
}
// 创建todo对象
const todo = { id: lastTodoId + 1, title: this.myInput.current.value, finished: false };
addOneTodo(todo);
// 清空
this.myInput.current.value = "";
}
}
}
Item.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class Item extends Component {
constructor(props) {
super(props)
this.state = {
showDelBtn: false
}
}
static propTypes = {
todo: PropTypes.object.isRequired, // 单条数据
removeTodoWithId: PropTypes.func.isRequired, // 方法
changeTodoFinished: PropTypes.func.isRequired // 选中方法
}
render() {
const { todo, removeTodoWithId, changeTodoFinished } = this.props;
const { showDelBtn } = this.state;
return (
<li
className="todos-item"
onMouseOver={() => this._showBtn(true)}
onMouseOut={() => this._showBtn(false)}
>
<label className="todos-item-title">
<input type="checkbox" checked={todo.finished} onChange={() => changeTodoFinished(todo.id, !todo.finished)} />
<span>{todo.title}</span>
</label>
<button onClick={() => removeTodoWithId(todo.id)} style={{ display: showDelBtn ? 'inline-block' : 'none' }}>删 除</button>
</li>
)
}
// 处理按钮显示隐藏
_showBtn(flag) {
this.setState({
showDelBtn: flag
})
}
}
List.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from './Item'
export default class List extends Component {
static propTypes = {
todos: PropTypes.array.isRequired, // 数据数组
removeTodoWithId: PropTypes.func.isRequired, // 删除方法
changeTodoFinished: PropTypes.func.isRequired // 选中方法
};
render() {
const { todos, removeTodoWithId, changeTodoFinished } = this.props;
return (
<ul>
{
todos.map((todo, index) => (
<Item
key={index}
todo={todo}
removeTodoWithId={removeTodoWithId}
changeTodoFinished={changeTodoFinished}
/>
))
}
</ul>
)
}
}
网友评论