TodoList (React编写)

目录结构

在这里插入图片描述

1. App

App.jsx

import { Component,createContext } from "react"
import Header from "./components/Header"
import List from "./components/List"
import Footer from "./components/Footer"
import "./App.css"

const CheckChange = createContext()

export default class App extends Component {
  // 初始化状态
  state = {
    todos:[
      {id:"001",name:"跳舞",done:true},
      {id:"002",name:"逛街",done:true},
      {id:"003",name:"健身",done:false},
    ]
  }
  addTodo = (todoObj)=>{
    const {todos} = this.state
    const newTodos = [todoObj,...todos]
    this.setState({
      todos:newTodos
    })
  }
  updateTodo = (id,done)=>{
    const { todos } = this.state
    const newTodo = todos.map((todo)=>{
      if (todo.id===id){
        return {...todo,id,done}
      } else return todo
    })
    this.setState({
      todos:newTodo
    })
  }

  deleteTodo = (id)=>{
    const { todos } = this.state
    let newTodo = todos.filter(todo=>{
      if (todo.id !== id){
        return todo
      }
    })
    this.setState({todos:newTodo})
  }
  checkAllTodo = (done)=>{
    const { todos } = this.state
    const newTodos = todos.map(todoObj=>{
      return {...todoObj,done}
    })
    this.setState({
      todos:newTodos
    })
  }
  clearAllDone = ()=>{
    const { todos } = this.state
    const newTodos = todos.filter(todo=>{
      return !todo.done
    })
    this.setState({
      todos:newTodos
    })
  }

  render (){
    const { todos } = this.state
    return(
      <div className="todo-container">
        <div className="todo-wrap">
          <Header addTodo={this.addTodo}/>
          <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
          <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
        </div>
      </div>
    )
  }
}

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

2. Header

index.jsx

import React, { Component } from 'react'
import { nanoid } from 'nanoid'
import "./index.css"

export default class Header extends Component {
  
  handleKeyUp = (e)=>{
    const { keyCode,target } = e
    // 回车键 编码值:13
    if(keyCode!==13) return
    if (target.value.trim() === ""){
      alert("输入的值不能为空!")
      return
    }
    let todoObj = {id:nanoid(),name:target.value,done:false}
    this.props.addTodo(todoObj)
    target.value = ""
  }
  render() {
    return (
        <div className="todo-header">
          <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
        </div>
    )
  }
}

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

3. List

index.jsx

import React, { Component } from 'react'
import Item from "../Item"
import "./index.css"

export default class List extends Component {
  render() {
    const {todos,updateTodo,deleteTodo} = this.props
    return (
      <ul className="todo-main">
        {
          todos.map(item=><Item key={item.id} {...item} updateTodo={updateTodo} deleteTodo={deleteTodo} />)
        }
      </ul>
    )
  }
}

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

4. Item

index.jsx

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)
    }
  }
  handleDel = (id)=>{
    if(confirm("确定删除吗?")){
      this.props.deleteTodo(id)
    }
  }

  render() {
    const { id,name,done} = this.props
    const { mouse } = this.state
    return (
      <li style={{backgroundColor:mouse?"#ccc":"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.handleDel(id)}} className="btn btn-danger" style={{display:mouse?"block":"none"}}>删除</button>
      </li>
    )
  }
}

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

5. Footer

index.jsx

import React, { Component } from 'react'
import "./index.css"

export default class Footer extends Component {
  handleCheckAll = (e)=>{
      this.props.checkAllTodo(e.target.checked)
  }
  clearCheckedAll = ()=>{
    this.props.clearAllDone()
  }

  render() {
    const { todos } = this.props
    // 完成数
    const doneCount = todos.reduce((pre,todo)=>{
      return pre + (todo.done ? 1 : 0)
    },0)
    const total = todos.length // 总数
    
    return (
      <div className="todo-footer">
        <label>
          <input type="checkbox" onChange={this.handleCheckAll} checked={doneCount===total&&total!==0?true:false} />
        </label>
        <span>
          <span>已完成{doneCount}</span> / 全部{total}
        </span>
        <button onClick={this.clearCheckedAll} className="btn btn-danger">清除已完成任务</button>
      </div>
    )
  }
}

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

总结TodoList

关于父子之间通信:
父传子:直接通过 props 传递
子传父:父组件通过 props 给子组件传递一个函数,子组件调用该函数

注意:

  1. defaultChecked(只生效一次) 和 checked 的区别,类似的还有:defaultValue 和 value
  2. state状态在哪里,操作状态的方法就在哪里
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个简单的todolist React组件的实现: ```jsx import React, { useState } from "react"; function TodoList() { const [todos, setTodos] = useState([]); const [inputValue, setInputValue] = useState(""); const handleInputChange = (e) => { setInputValue(e.target.value); }; const handleAddTodo = () => { if (inputValue.trim() !== "") { setTodos([...todos, inputValue]); setInputValue(""); } }; const handleRemoveTodo = (index) => { setTodos(todos.filter((_, i) => i !== index)); }; return ( <div> <h1>Todo List</h1> <input type="text" value={inputValue} onChange={handleInputChange} /> <button onClick={handleAddTodo}>Add</button> <ul> {todos.map((todo, index) => ( <li key={index}> {todo} <button onClick={() => handleRemoveTodo(index)}>x</button> </li> ))} </ul> </div> ); } export default TodoList; ``` 这个组件中,我们使用了`useState`钩子函数来管理组件的状态。我们使用了两个状态变量:`todos`表示待办事项列表,`inputValue`表示输入框的值。我们使用`handleInputChange`函数在输入框值变化时更新`inputValue`的值。使用`handleAddTodo`函数来添加新的待办事项。当点击“Add”按钮时,我们将`inputValue`的值添加到`todos`列表中,并清空输入框的值。使用`handleRemoveTodo`函数来移除待办事项。当点击待办事项后面的“x”按钮时,我们将该待办事项从`todos`列表中移除。最后,我们使用`map`函数遍历`todos`列表,并将每个待办事项渲染成一个列表项。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值