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.狀態在哪裏 操做狀態的方法就在哪裏 複製代碼
相關文章
相關標籤/搜索