react脚手架写todolist案例

1.创建一个react脚手架工程(此处省略创建过程)

注:此项目是根据B站react教学视频所做

2.分别在src中创建Footer,Header,Item,List文件夹

 3.在每一个文件夹中,创建一个index.css和index.jsx

 4.现在开始配置App.jsx和App.css

App.jsx

import './App.css';
import { Component } from 'react';
import Header from './Header';
import List from './List';
import Footer from './Footer';

export default class App extends Component{
// 状态在哪里,操作状态的方法就在哪里

  // 初始化状态
  state = {todos:[
    {id:'001',name:'吃饭',done:true},
    {id:'002',name:'睡觉',done:true},
    {id:'003',name:'打豆豆',done:false},
  ]}


  //addTodo用于添加一个todo,接收的参数是todo对象
	addTodo = (todoObj)=>{
		//获取原todos
		const {todos} = this.state
		//追加一个todo
		const newTodos = [todoObj,...todos]
		//更新状态
		this.setState({todos:newTodos})
	}
  
  // 勾选和取消勾选,用于更新一个对象
  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,接收的参数是todo对象
  deleteTodo = (id)=>{
    // 获取原来的todos
    const {todos} = this.state
    // 删除指定的id对象
    const newTodos = todos.filter((todoObj)=>{
      return todoObj.id !==id
    })

    // 更新状态
    this.setState({todos:newTodos})
  }

  // 全选
  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((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} />
         {/* 传数据 */}
         <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;
}



5.现在开始配置Footer

index.jsx

import React, { Component } from 'react'
import './index.css'
export default class  Footer extends Component {

  // 全选回调
  handleCheckAll = (event)=>{
     this.props.checkAllTodo(event.target.checked)
  }

  // 清除回调
  handleClearAllDone =()=>{
    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 className="btn btn-danger" onClick={this.handleClearAllDone}>清除已完成任务</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;
  }

6.现在开始配Header

index.jsx

import React, { Component } from 'react'
import './index.css'
import {nanoid} from 'nanoid'
import PropTypes from 'prop-types'

export default class Header extends Component {

  // 对接收的props进行限制
static propTypes = {
  addTodo:PropTypes.func.isRequired
}


//键盘事件的回调
handleKeyUp = (event)=>{
  //解构赋值获取keyCode,target
  const {keyCode,target} = event
  //判断是否是回车按键
  if(keyCode !== 13) return
  //添加的todo名字不能为空
  if(target.value.trim() === ''){
    alert('输入不能为空')
    return
  }
  //准备好一个todo对象
  const todoObj = {id:nanoid(),name:target.value,done:false}
  //将todoObj传递给App
  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);
  }

7.现在开始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 (event)=>{
        this.props.updateTodo(id,event.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':'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>
    )
  }
} 

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

8.开始List

index.jsx

import React, { Component } from 'react'
import Item from '../Item'
import './index.css'
import PropTypes from 'prop-types'
export default class List extends Component {

  // 控制类型
  static propTypes = {
    todos:PropTypes.array.isRequired,
    updateTodo:PropTypes.func.isRequired,
    deleteTodo:PropTypes.func.isRequired
  }

  render() {
    // 收数据
    const {todos,updateTodo,deleteTodo} =this.props
    return (
      <ul className="todo-main">
        {
          todos.map((todo)=>{
            return <Item key={todo.id} {...todo} 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;
  }

截至目前,所需要的东西都已经配置完,控制台输入npm start运行即可

效果如下

 todolist是一个经典的案例,希望大家能反复练习

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值