019.组件化编码流程实现TodoList

功能页面的组件化编码流程

  1. 拆分组件:拆分界面抽取组件
  2. 实现静态组件:使用组件实现静态页面效果
  3. 实现动态组件:
    • 动态展现初始化数据
      • 数据类型
      • 数据名称
      • 保存在那个组件
    • 交互(从绑定事件监听开始)

组件组合使用实现TodoList

效果图: image.pngcss

src文件夹结构图:
image.pngreact

demo代码

components/list

import React, { Component } from 'react'
import  Item from '../item/index'
import './index.css'
import PropTypes from 'prop-types'
export default class List extends Component {
    // 对接受的props进行类型和必要的限制
    static propTypes = {
        updateTodo:PropTypes.func.isRequired,
        deleteTodo:PropTypes.func.isRequired,
        todos:PropTypes.array.isRequired,
    }
    render() {
        const {todos,updateTodo,deleteTodo} = this.props
        console.log('todos',todos)
        return (
            <ul className='TodoListUl'> { todos.map(item => { return( <Item deleteTodo={deleteTodo} updateTodo={updateTodo} {...item} key={item.id}/> ) }) } </ul>
        )
    }
}

// css
.TodoListUl{
    margin: 0;
    padding: 0;
    width: 508px;
    margin-top: 10px;
}
复制代码

components/item

import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
    state={
        mouse:false
    }
    // 勾选
    handleCheck=(id)=>{
        return (e)=>{
            this.props.updateTodo(id,e.target.checked)
        }
    }

    // 鼠标移入移除
    handleMouse=(val)=>{
        return ()=>{
            this.setState({mouse:val})
        }
    }
    // 删除todo
    deleteTodo=(id)=>{
        return ()=>{
            const {deleteTodo} = this.props
            if(window.confirm('肯定删除么?')){
                    deleteTodo(id)
            }
        }
    }
    render() {
        const {id,name,done} = this.props
        const {mouse} = this.state
        return (
            <li style={{backgroundColor:mouse?'#ddd':'white'}} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)} className='todoListItem'> <span className='left'> <input checked={done} onChange={this.handleCheck(id)} type='checkbox'/> <span>{name}</span> </span> <button onClick={this.deleteTodo(id)} className='button' style={{display:mouse?'block':'none'}}>删除</button> </li>
        )
    }
}

//css
.todoListItem{
    list-style: none;
    display: flex;
    justify-content: space-between;
    height: 40px;
    line-height: 40px;
    border:1px solid #ddd;
}
.left{
    display: inline-block;
}
.button{
    margin: 5px 8px 0 0;
    border: none;
    height: 30px;
    width: 60px;
    border-radius: 4px;
    background-color: red;
    color: #fff;
    
}
复制代码

components/header

import React, { Component } from 'react'
import './index.css'
import PropTypes from 'prop-types'
export default class Header extends Component {
    // 对接受的props进行类型和必要的限制
    static propTypes = {
        addTodo:PropTypes.func.isRequired
    }

    // 键盘事件的回调
    handleKeyUp=(e)=>{
        // 解构keyCode target
        const {keyCode,target} = e
        // 回车键
        if(keyCode !== 13) return
        if(!target.value){
            alert('任务不能为空!')
            return
        }
        console.log('Enter')
        let todoObj = {
            id:this.getNums(),
            name:target.value,
            done:false
        }
        this.props.addTodo(todoObj)
        target.value = ''
    }
    
    getNums=()=>{
        return Math.round(Math.random()*200)
    }
    render() {
        return (
            <input onKeyUp={this.handleKeyUp} className='todoheader' placeholder='请输入你的任务,按回车键确认'/>
        )
    }
}

//css
.todoheader{
    width: 500px;
    height: 30px;
}
复制代码

components/footer

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

    // 全选
    handleCheckAll=(e)=>{
        this.props.handleCheckAll(e.target.checked)
    }
    // 清楚全部已完成
    handleClearAllDone=()=>{
        this.props.handleClearAllDone()
    }

    render() {
        const {todos} = this.props
        // reduce 数组条件统计 条件求和 筛选最值(MDN查看文档)

        // 已完成的个数
        const doneCount = todos.reduce((pre,todo)=>{ return pre+(todo.done?1:0)},0)
        // 所有个数
        const total = todos.length

        console.log('doneCount',doneCount)
        return (
            <div className='footerbox'> <input type='checkbox' onChange={this.handleCheckAll} checked={doneCount===total &&total!==0?true:false}/> <span className='checkboxSpan'>已完成{doneCount}/所有{total}</span> <button onClick={this.handleClearAllDone} className='deleteAllButton'>删除已完成任务</button> </div>
        )
    }
}

//css
.footerbox{
    margin-top: 20px;
    line-height: 40px;
}
.checkboxSpan{
    margin-left:20px;
}
.deleteAllButton{
    margin: 5px 8px 0 0;
    border: none;
    height: 30px;
    width: 120px;
    border-radius: 4px;
    background-color: red;
    color: #fff;
    float: right;
    
}
复制代码

App.js

import React,{Component} from 'react'
import Header from './components/header'
import List from './components/list'
import Footer from './components/footer'
import './App.css'
export default class App extends Component{
  // 状态在哪里,操做状态的方法就在哪里

  // 初始化状态
  state={
    todos:[
      {id:'001',name:'吃饭',done:true},
      {id:'002',name:'睡觉',done:true},
      {id:'003',name:'撸代码',done:false},
    ]
  }
  // 添加一个todo 获取的是todo对象
  addTodo=(data)=>{
    // 获取原todos数组
    const {todos} = this.state
    // 追加一个todo对象
    const newTodos = [data,...todos]
    this.setState({todos:newTodos})
  }

  // 修改完成未完成状态
  updateTodo=(id,done)=>{
    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)=>{
    const {todos} = this.state
    const newTodos = todos.filter(item=>{
      return item.id !== id
    })
    this.setState({todos:newTodos})
  }

  // 全选
  handleCheckAll=(done)=>{
    const {todos} = this.state
    // 加工数据
    const newTodos = todos.map(item=>{
      return {...item,done}
    })
    this.setState({todos:newTodos})
  }
  // 清除全部已完成
  handleClearAllDone=()=>{
    const {todos} = this.state
    // 加工数据
    const newTodos = todos.filter(item=>{
      return item.done === false
    })
    this.setState({todos:newTodos})
  }

  render(){
    const { todos } = this.state
    console.log('apptodos',todos)
    return (
      <div className='todoListBox'> <h2>豆豆是个小呆瓜</h2> <Header addTodo={this.addTodo}/> <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/> <Footer handleClearAllDone={this.handleClearAllDone} handleCheckAll={this.handleCheckAll} todos={todos}/> </div>
    )
  }
}

//css
.todoListBox{
    width: 520px;
    padding: 10px 0px 20px 10px;
    border: 2px solid #ddd;
    margin: auto;
    border-radius: 6px;
}
复制代码

index.js

// 引入React核心库
import React from 'react'
// 引入ReactDOM
import ReactDOM from 'react-dom'
// 引入App组件
import App from './App'

// 渲染App到页面
ReactDOM.render(<App/>,document.getElementById('root'))
复制代码

总结

1.拆分组件、实现静态组件,注意:className style的写法
2.动态初始化列表,如何肯定将数据放在哪一个组件的state中?
 -某个组件使用:放在自身的state中 -某些组件使用:放在他们共同的父组件state中(状态提高) 3.关于父子之间通讯: 1.父组件给子组件传递数据:props传递 2.子组件给父组件传递数据:父组件经过props给子组件传递一个函数 4.注意defaultChecked和checked的区别 相似还有defaultValue和value 5.状态在哪里 操做状态的方法就在哪里 复制代码
相关文章
相关标签/搜索