• 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 (
                
    {/*向子组件传递一个函数*/} {/*传值给子组件*/}
    ); } } export default App;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    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 (
                
    ) } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    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 (
                
      {todos.map((item) => { return })}
    ) } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    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 (
                
  • {backgroundColor: mouse ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
  • ) } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    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);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
  • 相关阅读:
    推荐 4 个开源小程序
    Spring6.1之RestClient分析
    GoLong的学习之路(二十二)进阶,语法之并发(go最重要的特点)(channel的主要用法)
    【我的百度实习总结】百度网盘——一刻相册实习
    不用担心JDK17收费了,Oracle 推出 JDK 8 的升级替代品
    springboot整合sentinel完成限流
    12-输入/输出项目构建命令行程序
    微擎模块 超人名片 1.3.5 原版后台+前端小程序源码,新增他人名片页IM实时聊天功能
    FPGA——三速自适应以太网设计(2)GMII与RGMII接口
    Python-使用sqlite3模块
  • 原文地址:https://blog.csdn.net/fangqi20170515/article/details/126180156