react實現todolist的增刪改查詳解

以todolist為例

在這裡插入圖片描述

在這裡插入圖片描述

目錄如下

在這裡插入圖片描述

app.js

 import React, {  PureComponent } from 'react'
import Input from './components/Input'
import List from './components/List'
import Total from './components/Total'
import Mask from './components/Mask'
import { bus as $bus } from './components/bus'
import './App.css'
export default class App extends PureComponent {
  constructor() {
    super()
    this.state = {
      flag: false,
      list: [
        {
          id: 1,
          content: '哈哈哈',
          checked: false
        },
        {
          id: 7,
          content: '哈哈哈',
          checked: false
        },
        {
          id: 5,
          content: '哈哈哈',
          checked: false
        },
      ],
      checkAll: false,
      selectLength: 0,
      item: {}
    }
  }
  // 全選全不選
  checkAllHandler(checked) {
    console.log("checked",checked);
    const { list } = this.state
    const newList = list.map(item =>{
      return {...item,checked}
    })
    this.setState({list:newList,checkAll: checked},()=>{
      this.doneLenth()
    })
  }
  // 單選單不選
  checkHandler =(id,checked)=> {
    const { list } = this.state
    const newList = list.map(item => {
      return item.id === id ? {...item,checked} : item
    })
    let checkAll = newList.length && newList.every(item => item.checked)
    this.setState(() => ({list: newList,checkAll}),()=>{
      this.doneLenth()
    })
  }
  // 添加 
  addHandler = (obj)=>{
    let { list } = this.state;
    let newList = [...list,obj]
    console.log('newList===='+newList)
    this.setState({
      list: newList,
    },()=>{
      this.doneLenth()
    })
  } 
  // 搜索
  searchHandler=(content)=>{
    console.log("content",content);
    let { list } = this.state;
    let newList = list.filter(item => item.content.includes(content))
    this.setState({
      list: newList
    },()=>{
      this.doneLenth()
    })
  }
  // 刪除
  delHandler = (id)=> {
    console.log("id",id);
    const { list } = this.state
    const newList = list.filter(item => item.id !=id)
    let checkAll = newList.length && newList.every(item => item.checked)
    this.setState(() => ({list: newList,checkAll}),()=>{
      this.doneLenth()
    })
  }
  // 編輯
  editHandler = (items)=>{
    this.setState({
      item: items
    })
  }
  // 更新
  update = (content)=>{
    const { list,item } = this.state
    let obj = Object.assign(item,{content})
    const newList = list.map(v =>{
      if(v.id === obj.id) {
        v = {...obj}
      }
      return v
    })
    this.setState({
      list: newList,
      item: obj
    })
  }
  // 已完成 
  doneLenth=()=> {
    const { list } = this.state
    const newList = list.filter(item => item.checked)
    let selectLength = newList.length
    setTimeout(()=>{
      this.setState({
        selectLength
      })
    })
  }
  // 掛載
  componentDidMount() {
    this.unSubscribe = $bus.addListener("getFlag",(flag)=>{
      this.setState({flag})
    })
    this.unSubscribe1 = $bus.addListener("sendValue",(obj)=>{
     this.addHandler(obj)
    })
    this.unSubscribe2 = $bus.addListener("searchValue",(value)=>{
     this.searchHandler(value)
    })
    this.unSubscribe3 = $bus.addListener("getItem",(item)=>{
     this.editHandler(item)
    })
    this.unSubscribe4 = $bus.addListener("update",(content)=>{
     this.update(content)
    })
  }
  // 卸載
  componentWillUnmount() {
    $bus.removeListener(this.unSubscribe)
    $bus.removeListener(this.unSubscribe1)
    $bus.removeListener(this.unSubscribe2)
    $bus.removeListener(this.unSubscribe3)
    $bus.removeListener(this.unSubscribe4)
  }
  render() {
    let { flag, list,checkAll,selectLength } = this.state
    return (
      <div className='container'>
        {/* 輸入框 */}
        <Input></Input>
        {/* 列表 */}
        <List list={list} checkHandler={this.checkHandler} delHandler={this.delHandler}></List>
        {/* 統計 */}
        <Total checkAllHandler={this.checkAllHandler.bind(this)} checkAll={checkAll} selectLength={selectLength}></Total>
        {/* 編輯彈框 */}
        { flag ? <Mask ></Mask> : ''}
      </div>
    )
  }
}

Input.js

import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class Input extends Component {
  constructor() {
    super()
    this.state = {
      value:""
    }
  }
  changeHandler = (e)=>{
    this.setState({
      value: e.target.value
    })
    console.log("this.state.value",this.state.value);
  }
  // 添加
  addHandler = ()=>{
    let { value } = this.state;
    let obj = {
      id: Date.now(),
      content: value,
      done: false
    }
    if(value) {
      $bus.emit("sendValue",obj)
    } else {
      console.log("請輸入")
    }
  }
  // 搜索
  searchHandler = ()=>{
    console.log("搜索");
    let { value } = this.state;
    if(!value) return console.log("請輸入");
    $bus.emit("searchValue",value)
  }
  render() {
    let { value } = this.state
    return (
      <>
        <div className="input">
          <input type="text" value={value} placeholder='請輸入你的任務名稱,按回車鍵確認' onInput={this.changeHandler}/>
          <button className="btn btn-success" onClick={this.addHandler}>添加</button>
          <button className="btn btn-primary" onClick={this.searchHandler}>搜索</button>
        </div>
      </>
    )
  }
}

List.js

import React, { Component } from 'react'
import Item from './Item'
import PropTypes from 'prop-types'
export default class List extends Component {
  static propTypes = {
		list:PropTypes.array.isRequired,
	}
  render() {
    let { list,checkHandler,checkAllHandler,delHandler } = this.props;
    console.log("list",list);
    return (
      <ul className="task-list">
        {
          list.map(item => (<Item item={item} key={item.id} checkHandler={checkHandler} checkAllHandler={checkAllHandler} delHandler={delHandler}></Item>))
        }
      </ul>
    )
  }
}

Item.js

import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class Item extends Component {
  constructor(props) {
    super(props)
    this.state = {}
  }
  changeHandler = (id)=>{
    let { checkHandler } = this.props;
    return (e)=>{
      checkHandler(id,e.target.checked)
    }
  }
  removeHandler(){
    let { delHandler } = this.props;
    delHandler(arguments[0])
  }
  editHadnler = (item)=>{
    $bus.emit("getFlag",true)
    localStorage.setItem("obj",JSON.stringify(item))
    $bus.emit("getItem",item)
  }
  render() {
    let { item } = this.props;
    return (
      <li className="task-item">
        <input type="checkbox" checked={item.checked} onChange={this.changeHandler(item.id)}/>
        <div className="content">
          {item.content}
        </div>
        <button className={`btn btn-success ${!item.checked ? "d-none" : "d-block"}`} onClick={()=> this.editHadnler(item)}>編輯</button>
        <button className={`btn btn-danger ${!item.checked ? "d-none" : "d-block"}`} onClick={this.removeHandler.bind(this,item.id)}>刪除</button>
      </li>
    )
  }
}

Total.js

import React, { Component } from 'react'
export default class Total extends Component {
  constructor() {
    super()
    this.changeAllHandler = this.changeAllHandler.bind(this)
  }
  changeAllHandler(e) {
    let { checkAllHandler } = this.props
    checkAllHandler(e.target.checked)
  }
  render() {
    let { checkAll,selectLength } = this.props;
    return (
      <div className="task-done">
        <input type="checkbox" onChange={this.changeAllHandler} checked={checkAll}/>
        <p>已完成<span className="single-number">{selectLength}</span> 全部<span className="all-number">4</span></p>
      </div>
    )
  }
}

Mask.js(彈窗)

import React, { Component } from 'react'
import { bus as $bus } from './bus'
export default class mask extends Component {
  constructor() {
    super()
    this.state = {
      value: ''
    }
  }
  closeMask = ()=>{ // 關閉彈窗
    $bus.emit("getFlag",false)
  }
  updateHandler = ()=>{
    $bus.emit("getFlag",false)
    $bus.emit("update",this.state.value)
  }
  onChange = (e) =>{
    this.setState({
      value: e.target.value
    })
  }
  componentDidMount() {
    let obj = JSON.parse(localStorage.getItem("obj"))
    this.setState({
      value: obj.content
    })
  }
  render() {
    let { value } = this.state
    return (
      <div>
        <div className="mm-mask" >
        <div className="mm-modal">
          <div className="mm-title">
            <span className="mm-edit">編輯</span>
            <span className="mm-close" onClick={this.closeMask}>x</span>
          </div>
          <div className="mm-content">
            <input type="text" value={value} placeholder="任務名稱" onInput={this.onChange}/>
          </div>
          <div className="mm-box-btn">
            <div className="mm-update" onClick={this.updateHandler}>更新</div>
            <div className="mm-cancel" onClick={this.closeMask}>取消</div>
          </div>
        </div>
      </div>
      </div>
    )
  }
}

bus.js

 yarn add -D events
import { EventEmitter } from 'events'
export const bus = new EventEmitter() // 導出bus實例

App.css

* {
  margin: 0;
  padding: 0;
}
input,button {
  outline: none;
  border: 0;
}
ul>li {
  list-style: none;
}
.container {
  width: 400px;
  height: 500px;
  margin: 10px auto auto;
  padding: 20px;
  box-sizing: border-box;
  color: #3333;
  border: 1px solid;
  overflow: hidden;
}
.input {
  width: 100%;
  height: 30px;
  display: flex;
}
input {
  width: 100%;
  height: 100%;
  border: 1px solid #e1e1e1;
  box-sizing: border-box;
  border-radius: 4px;
  padding: 0 10px;
}
input::placeholder {
  color: #e1e1e1;
}
input:focus {
  border: 1px solid #0096e6;
}
.task-list {
  width: 100%;
  display: flex;
  flex-flow: column wrap;
  margin-top: 10px;
}
.task-list li {
  display: flex;
  height: 40px;
  justify-content: center;
  align-items: center;
  padding: 0 10px;
  background-color: #eef0f4;
  margin-bottom: 10px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.task-list li input[type^="checkbox"] {
  width: 15px;
  height: 15px;
  border: 1px solid #e1e1e1;
  cursor: pointer;
  flex-shrink: 0;
}
.task-list li .content {
  flex: 1;
  margin-left: 10px;
}
.btn {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  height: 30px;
  justify-content: center;
  padding: 5px 10px;
  text-align: center;
  cursor: pointer;
  border-radius: 4px;
  color: #fff;
  letter-spacing: 2px;
  margin: 0 5px;
  box-sizing: border-box;
  font-size: 16px;
}
.btn-success {
  background-color: #0f0;
}
.btn-danger {
  background-color: #f00;
}
.btn-primary {
  background-color: #0096e6;
}
.task-done {
  width: 100%;
  height: 40px;
  line-height: 40px;
  display: flex;
  align-items: center;
  background-color: #eef0f4;
  padding-left: 10px;
  box-sizing: border-box;
  margin-top: 30px;
}
.task-done input {
  width: 15px;
  height: 15px;
  border: 1px solid #e1e1e1;
  cursor: pointer;
  flex-shrink: 0;
  margin-right: 10px;
}
.single-number {
  color: #333;
  margin-left: 5px;
}
.all-number {
  color: red;
  margin-left: 5px;
}
.mm-mask{
  position:fixed;
  top:0;
  left:0;
  right:0;
  bottom:0;
  background:rgba(0,0,0,0.5);
}
.mm-modal{
  width:350px;
  position:absolute;
  top:50%;
  left:50%;
  transform:translate(-50%,-50%);
  z-index:1000;
  background:#ffffff;
  border-radius:4px;
  color:#333333;
}
.mm-title {
  height:50px;
  line-height:50px;
  display:flex;
  justify-content:space-between;
  border-bottom:1px solid #e1e1e1;
  box-sizing:border-box;
  font-size:20px;
}
.mm-edit{
  text-indent:20px;
}
.mm-close{
  margin-right:20px;
  font-family:consals;
  cursor:pointer;
}
.mm-content{
  padding:0 20px;
  margin-bottom:20px;
}
.mm-content input{
  width:100%;
  height:30px;
  line-height:30px;
  text-indent:20px;
  border-radius:4px;
  margin-top:20px;
  border:1px solid #666;
  box-sizing:border-box;
}
.mm-content input:hover{
  border:1px solid #0096e6;
}
.mm-content input:last-child{
  text-indent:5px;
}
.mm-box-btn{
  display:flex;
}
.mm-update,.mm-cancel{
  width:80px;
  height:30px;
  line-height:30px;
  text-align: center;
  cursor:pointer;
  background:#0096e6;
  color:#ffffff;
  user-select:none;
  border-radius:4px;
  margin:0 20px 50px;
}
.mm-update{
  margin-right:10px;
}
.d-none {
  display: none;
}
.d-block {
  display: block;
}

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: