react实现todoList案例

运行效果

在这里插入图片描述

项目目录结构

在这里插入图片描述

App.js

import Header from "./components/header/Header";
import List from "./components/list/List";
import Footer from "./components/footer/Footer";
import './App.css';

import React, {Component} from 'react';

class App extends Component {
    //状态在哪里,操作状态的方法就在哪里
    state = {
        todos: [
            {id: 1, name: '吃饭', done: true},
            {id: 2, name: '睡觉', done: true},
            {id: 3, name: '码代码', done: false},
            {id: 4, name: '逛街', done: false}
        ]
    }
    //用于添加一个todo,接收的参数是todo对象
    addTodo = (todoObj) => {
        //获取原来的todos
        const {todos} = this.state
        //    追加一个todos
        const newTodos = [todoObj, ...todos];
        //    更新状态
        this.setState({
            todos: newTodos
        })
    }

    //更新一个todo的完成状态
    updateTodo = (id, done) => {
        //获取状态中的todos
        const {todos} = this.state;
        //匹配处理数据
        const newTodos = todos.map((item) => {
            if (item.id === id) return {...item, done}
            else return item
        })
        //    更新状态
        this.setState({todos: newTodos})
    }

    //删除todo
    deleteTodo = (id) => {
        //获取状态中的todos
        const {todos} = this.state;
        //    删除指定id的元素
        const newTodos = todos.filter((item) => {
            return item.id !== id
        })
        //    更新状态
        this.setState({todos: newTodos})
    }
    //全选
    checkAllTodo = (done) => {
        //获取状态中的todos
        const {todos} = this.state;
        //    删除指定id的元素
        const newTodos = todos.map((item) => {
            return {...item, done}
        })
        //    更新状态
        this.setState({todos: newTodos})
    }
    //清除所有已完成的
    clearAllDone = () => {
    //获取状态中的todos
        const {todos} = this.state;
        //    删除指定id的元素
        const newTodos = todos.filter((item) => {
            return item.done === false
        })
        //    更新状态
        this.setState({todos: newTodos})
    }

    render() {
        return (
            <div className="todo-container">
                <div className="todo-wrap">
                    <Header onAddTodo={this.addTodo}/>{/*向子组件传递一个函数*/}
                    <List todos={this.state.todos} onUpdateTodo={this.updateTodo}
                          onDeleteTodo={this.deleteTodo}/>{/*传值给子组件*/}
                    <Footer todos={this.state.todos} onClearAllDone={this.clearAllDone}
                            onCheckAllTodo={this.checkAllTodo}/>
                </div>
            </div>
        );
    }
}

export default App;

Head.js


import React, {Component} from 'react';
//引入这个库,使用nanoid,可以确保id唯一
import {nanoid} from "nanoid";
import PropTypes from "prop-types";
import "./header.css";


export default class Header extends Component {
    //对接收的props进行:类型、必要性限制
    static propTypes = {
        onAddTodo: PropTypes.func.isRequired
    }

    //鼠标按下时添加任务
    handleKeyUp = (e) => {
        const {value} = e.target
        if (e.keyCode !== 13) return
        console.log(value);
        if (value.trim() === '') {
            alert('输入不能为空');
            return;
        }
        //准备好一个todo对象
        const todoObj = {id: nanoid(), name: value, done: false};
        //    执行父组件传过来的函数
        this.props.onAddTodo(todoObj);
        //    清空输入框
        e.target.value = '';
    }

    render() {
        return (
            <div className="todo-header">
                <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
            </div>
        )
    }
}

List.js

/**
 * @name: List
 * @Description:
 * @Author: 方琦
 * @Date: 2022/8/1 10:47
 **/
import React, {Component} from 'react';
import PropTypes from "prop-types";
import Item from "../item/Item";
import "./list.css"

export default class List extends Component {
    //对接收的props进行:类型、必要性限制
    static propTypes = {
        todos:PropTypes.array.isRequired,
        onUpdateTodo: PropTypes.func.isRequired,
        onDeleteTodo: PropTypes.func.isRequired
    }

    render() {
        //接收父组件传递过来的参数
        const {todos,onUpdateTodo,onDeleteTodo} = this.props
        return (
            <ul className="todo-main">
                {todos.map((item) => {
                    return <Item {...item} key={item.id} onUpdateTodo={onUpdateTodo} onDeleteTodo={onDeleteTodo}/>
                })}
            </ul>
        )
    }
}

item.js


import React, {Component} from 'react';
import PropTypes from "prop-types";
import './item.css';

export default class Item extends Component {
    state = {mouse: false}
    //对接收的props进行:类型、必要性限制
    static propTypes = {
        onUpdateTodo: PropTypes.func.isRequired,
        onDeleteTodo: PropTypes.func.isRequired
    }
    //设置鼠标移入移出的样式
    handleMouse = (flag) => {
        return () => {
            this.setState({
                mouse: flag
            })
        }
    }
    //勾选,取消todo的状态
    handleCheck = (id) => {
        return (e) => {
            this.props.onUpdateTodo(id, e.target.checked)
        }
    }
    //删除todo
    handleDelete = (id) => {
        //注意要使用window.confirm
        if (window.confirm('确定删除么?')) {
            this.props.onDeleteTodo(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>
                    <input type="checkbox" checked={done} onChange={this.handleCheck(id)}/>
                    <span>{name}</span>
                </label>
                <button onClick={() => this.handleDelete(id)} className="btn btn-danger"
                        style={{display: mouse ? 'block' : 'none'}}>删除
                </button>
            </li>
        )
    }
}

Footer.js

import React, {Component} from ‘react’;
import ‘./footer.css’;
import PropTypes from “prop-types”;

export default class Footer extends Component {
static propTypes = {
onCheckAllTodo: PropTypes.func.isRequired,
onClearAllDone: PropTypes.func.isRequired
}
//全选
handleCheckAll = (e) => {
this.props.onCheckAllTodo(e.target.checked)
}
//清除所有已完成的todo
handleClearAllDone=()=>{
this.props.onClearAllDone()
}
render() {
const {todos} = this.props;
//计算已完成的数量
const doneCount = todos.reduce((pre, item) => {
return pre + (item.done ? 1 : 0)
}, 0);
//总条数
const total = todos.length
return (



已完成{doneCount} / 全部{todos.length}
清除已完成任务

)
}
}

App.css
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

header.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);
}

list.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.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;
}

footer.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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值