整体流程思路
- 组件拆分:header list>item footer
- 布局出静态代码
- 书写事件处理函数(数据在哪里、具体就在哪里修改)、这里数据全部放在 最外层APP组件、内部子组件触发回调(传入参数) 实际调用 app组件的处理函数(使用props 把函数传入进去)
- 具体操作为 添加数据、修改数据状态、删除数据、全选反选状态、清除已选状态
- 还可以继续优化、如数据存放在浏览器本地存储当中...
App.jsx 数据和方法都定义在这里 向子组件传递
import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import Header from './component/Header';
import List from './component/List';
import Footer from './component/Footer';
import './App.css';
export default class App extends Component {
state = {
todoData:[
{id:'001',done:true,context:'吃饭'},
{id:'002',done:true,context:'睡觉'},
{id:'003',done:false,context:'敲代码'}
]
}
addTodoData = (context) =>{
const newObj = {id:nanoid(),context:context,done:false}
this.setState({
todoData:[newObj,...this.state.todoData]
})
}
editSele = (done,id) =>{
const newA = this.state.todoData.map(item =>{
if(item.id === id) {
return {...item,done}
}else return item
})
this.setState({
todoData:newA
})
}
clearItem = (id) =>{
if(window.confirm('确定要删除吗?')){
const upData = this.state.todoData.filter(x =>{
return x.id !== id
})
this.setState({
todoData:upData
})
}else return
}
changeAllSele = (done) =>{
const newA = this.state.todoData.map(item =>{
return {...item,done}
})
this.setState({
todoData:newA
})
}
layoffDone = () =>{
const load = this.state.todoData.filter(x =>{
return x.done === false
})
this.setState({
todoData:load
})
}
render(){
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodoData={this.addTodoData}></Header>
<List todoData={this.state.todoData} editSele={this.editSele} clearItem={this.clearItem}></List>
<Footer todoData={this.state.todoData} changeAllSele={this.changeAllSele} layoffDone={this.layoffDone}></Footer>
</div>
</div>
);
}
}
Header.jsx 有添加功能 传入输入框的数值 触发传进来的函数
import React, { Component } from 'react'
import './index.css'
import PropTypes from 'prop-types'
export default class Header extends Component {
static propTypes = {
addTodoData:PropTypes.func.isRequired
}
handleKeyUp = (e) =>{
if(e.keyCode !==13) return
if(e.target.value.trim() === ''){
alert('输入内容不能为空')
return
}
const context = e.target.value.trim()
this.props.addTodoData(context)
e.target.value=''
}
render() {
return (
<div className="todo-header">
<input type="text" onKeyUp={this.handleKeyUp} placeholder="请输入你的任务名称,按回车键确认"/>
</div>
)
}
}
List.jsx 整体列表 再 map return 每一项
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from './Item'
export default class List extends Component {
static propTypes = {
todoData:PropTypes.array.isRequired,
editSele:PropTypes.func.isRequired,
clearItem:PropTypes.func.isRequired
}
render() {
return (
<ul className="todo-main">
{
this.props.todoData.map(todo =>{
return <Item key={todo.id} {...todo} editSele={this.props.editSele} clearItem={this.props.clearItem}></Item>
})
}
</ul>
)
}
}
Item.jsx 每一项列表 有修改状态 和 删除当前项 传入ID 触发传进来的函数
还给每个li 都添加了 鼠标移入事件 通过布尔值和 三元表达式 修改显示状态
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = {
mouse:false
}
throughItem = (flag) =>{
return () =>{
this.setState({mouse:flag})
}
}
changeSele = (id) =>{
return (e) =>{
this.props.editSele(e.target.checked,id)
}
}
killItem = (id) =>{
return () =>{
this.props.clearItem(id)
}
}
render() {
const{id,done,context} = this.props
const {mouse} = this.state
return (
<li onMouseEnter={this.throughItem(true)} onMouseLeave={this.throughItem(false)} style={mouse ? {background:'#f0f0f0'} : {background:'#fff'}}>
<label>
<input type="checkbox" checked={done} onChange={this.changeSele(id)}/>
<span>{context}</span>
</label>
<button className="btn btn-danger" style={mouse ? {display:'block'} : {display:'none'}} onClick={this.killItem(id)}>删除</button>
</li>
)
}
}
footer.jsx 底部组件 reduce方法计算出数据中已选的项、把点击全选后的状态传递出去 删除已选的项不用传参 直接调用父组件函数
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
changeAllSele1 = (e) =>{
this.props.changeAllSele(e.target.checked)
}
layoff = () =>{
this.props.layoffDone()
}
render() {
const {todoData} = this.props
let total = todoData.length
let sele = todoData.reduce((a,b) =>a + (b.done?1:0),0)
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={this.changeAllSele1} checked={total===sele&&total!==0?true:false} />
</label>
<span>
<span>已完成{sele}</span> / 全部{total}
</span>
<button className="btn btn-danger" onClick={this.layoff}>清除已完成任务</button>
</div>
)
}
}