react学习——17react中todoList案列

发布于:2024-06-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

1、项目目录
在这里插入图片描述
2、App.js

//创建“外壳”组件APP
import React, {Component} from "react";
//引入Header组件
import Header from "./components/Header";
//引入List组件
import List from "./components/List";
//引入Footer组件
import Footer from "./components/Footer";
//引入样式
import "./App.css";
//创建并暴露App组件
export default class App extends Component {
    //状态在哪里,操作状态的方法就在哪里
    state={
        todos:[
            {id:1,name:'吃饭',done:true},
            {id:2,name:'睡觉',done:false},
            {id:3,name:'打代码',done:true},
            {id:4,name:'逛街',done:false},
        ]
    }
    //用于添加todo ,接收的参数是todo对象
    addTodo=(todoObj)=>{
        const {todos} = this.state;
        //追加一个todo
        const  newTodos = [todoObj,...todos];
        //更新状态
        this.setState({todos:newTodos})
    }
    //用于更新todo
    updateTodo=(id,done)=>{
        //获取状态中的todos
        const {todos} = this.state;
        //匹配处理数据
        const newTodos = todos.map((todoObj)=>{
            if (todoObj.id===id) return {...todoObj,done}
            else return todoObj
        })
        this.setState({todos:newTodos})
    }
    //deleteTodo 删除todo
    deleteTodo=(id)=>{
        //获取状态中的todos
        const {todos} = this.state;
        //删除指定id的todo
        const newTodos = todos.filter((todoObj)=>{
            return todoObj.id!==id
        })
        this.setState({todos:newTodos})
    }
    //用于全选或者全不选
    checkAllTodo = (done)=>{
        //获取原来的Todos
        const {todos} = this.state
        //加工数据
        const newTodos = todos.map((todoObj)=>{
            return {...todoObj,done}
        })
        //更新状态
        this.setState({todos:newTodos})
    }
    //清除已完成的todo
    handleClearAll=()=>{
        const {todos} = this.state;
        const newTodos = todos.filter((todoObj)=>{
            return !todoObj.done
        })
        this.setState({todos:newTodos})
    }
    render() {
        const {todos} = this.state;
        return (
            <div className="todo-container">
                <div className="todo-wrap">
                    <Header addTodo={this.addTodo}></Header>
                    <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}></List>
                    <Footer handleClearAll={this.handleClearAll} checkAllTodo={this.checkAllTodo} todos={todos}></Footer>
                </div>
            </div>
        )
    }
}

3、App.css

/*base*/
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;
}

4、Foot组件
4.1、index.jsx

//创建“外壳”组件APP
import React, {Component} from "react";
//引入样式
import "./index.css";
//创建并暴露App组件
export default class Footer extends Component {
    //全选
    checkAllTodo = (e)=>{
        this.props.checkAllTodo(e.target.checked);
    }
    handleClearAll = ()=>{
        this.props.handleClearAll();
    }
    render() {
        const {todos} = this.props;
        //获取已完成的任务数量
        const completedCount = todos.reduce((preValue,todo)=>{
            return preValue + (todo.done ? 1 : 0)
        },0)
        //总数
        const totalCount = todos.length;
        return (
            <div className="todo-footer">
                <label>
                    <input onChange={this.checkAllTodo} type="checkbox" checked={completedCount === totalCount && totalCount !== 0}/>
                </label>
                <span>
                    <span>已完成{completedCount}</span> / 全部{totalCount}
                </span>
                <button onClick={this.handleClearAll} className="btn btn-danger">清除已完成任务</button>
            </div>
        )
    }
}

4.2、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;
}

5、Header组件
5.1、index.jsx

//创建“外壳”组件APP
import React,{Component} from "react";
import PropTypes  from "prop-types";
//引入naono
import {nanoid} from "nanoid";
//引入样式
import "./index.css"
export default class Header extends Component{
    //对接收的props进行类型检查
    static propTypes = {
        addTodo:PropTypes.func.isRequired
    }
    addItem=(e)=>{
        //结构赋值
        const {keyCode,target} = e
        //判断是否是回车键
        if (keyCode!==13)return
        if(target.value.trim()===''){
            alert('输入不能为空')
            return;
        }
        //准备好一个新的todo对象
        const todoObj = {
            id:nanoid(),
            name:target.value,
            done:false
        }
        this.props.addTodo(todoObj)
        target.value=''
    }
    render(){
        return(
            <div className="todo-header">
                <input onKeyUp={this.addItem} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
            </div>
        )
    }
}

5.2、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);
}

6、Item组件
6.1、index.jsx

//创建“外壳”组件APP
import React,{Component} from "react";
//引入样式
import "./index.css";
export default class Item extends Component{
    state={
        mouse:false
    }
    handleMouse=(flag)=>{
        return ()=>{
            this.setState({
                mouse:flag
            })
        }
    }
    handleCheck=(id)=>{
        return (e)=>{
            this.props.updateTodo(id,e.target.checked)
        }
    }

    handleDelete=(id)=>{
        if(window.confirm('是否删除')){
            this.props.deleteTodo(id)
        }
    }
    render(){
        const {id,name,done} = this.props
        const {mouse} = this.state
        return(
            <li style={{backgroundColor:mouse?'#ddd':''}} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
                <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>
        )
    }
}

6.2、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;
}

7、List组件
7.1、index.jsx

//创建“外壳”组件APP
import React,{Component} from "react";
import PropTypes from "prop-types";
//引入Item组件
import Item from "../Item";
//引入样式
import './index.css'
export default class List extends Component{
    static propTypes = {
        todos:PropTypes.array.isRequired,
        updateTodo:PropTypes.func.isRequired
    }
    render(){
        const {todos,updateTodo,deleteTodo} = this.props;
        return(
            <ul className="todo-main">
                {
                    todos.map((item,index)=>{
                        return <Item key={item.id} {...item} updateTodo={updateTodo} deleteTodo={deleteTodo}></Item>
                    })
                }
            </ul>
        )
    }
}

7.2、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;
}

8、实现效果

在这里插入图片描述