react案例1:todoList

案例效果:

一、App组件和header输入框的交互:

需求:header输入框中输入活动项,回车之后添加在app组件的state状态中

实现:1、通过在App父组件中添加一个事件,子组件中可以通过this.props.a的方法触发;

2、子组件中调用方法并通过参数传递需要增加的数据

(1).App组件

javascript 复制代码
import React, { Component } from 'react'
import Header from './components/Header';
import List from './components/List';
import Footer from './components/Footer';
import './App.css';

class App extends Component {
  state={todos:[
    {id:'001',name:'吃饭',done:true},
    {id:'002',name:'睡觉',done:true},
    {id:'003',name:'打代码',done:false},
  ]}
   a=(info)=>{
     const{todos}=this.state
     const newTodos=[info,...todos]
     console.log('this',this)
    //  修改状态
     this.setState({todos:newTodos})
  }
 render(){
    return (
        <div className="App">
          <div className="todo-container">
            <div className="todo-wrap">
             <Header a={this.a}/>
             <List {...this.state}/>
             <Footer/>
          </div>
         </div>
        </div>
      );
 }
}

export default App;

(2)header组件

javascript 复制代码
import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import './index.css'
export default class Header extends Component {
    addMessage=(event)=>{
        const {keyCode,target}=event
        if(keyCode===13){
            if(!target.value.trim()){
                alert('输入不能为空')
                return
            }
            console.log(target.value)
            const info={id:nanoid(),name:target.value,done:false}
            this.props.a(info)
        }
    }
    render() {
      return (
        <div>
           <div className="todo-header">
             <input type="text" onKeyUp={this.addMessage} placeholder="请输入你的任务名称,按回车键确认"/>
           </div>
        </div>
    )
  }
}
二、实现鼠标移入移出展示效果
javascript 复制代码
import React, { Component } from 'react'
import './index.css'

export default class Item extends Component {
    state={mouse:false}
   handleMouse=(flag)=>{
     return ()=>{
        console.log(flag)
        this.setState({mouse:flag})
        console.log('mouse',this.state.mouse)
     }
  }
  render() {
    const {name,done}=this.props
    const {mouse}=this.state
    return (
      <div>
        {/* defaultChecked只管一上来是不是勾选,如果是checked则是后续不能修改 */}
       {
        <li style={{backgroundColor:mouse?'#ddd':'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
          <label>
            <input type="checkbox" defaultChecked={done}/>
            <span>{name}</span>
          </label>
          <button className="btn btn-danger" style={{display:mouse?'block':'none'}}>{'删除'}</button>
        </li>
       }
        
      </div>
    )
  }
}
三、选项框修改App中的状态(APP与item爷孙交互)

总结:状态在哪里,修改状态的方法得在哪里。

实现步骤:

1.App中在子组件实例创建的位置绑定事件,并通过方法修改状态中的数据。

2.中间的子组件List中继续传递props中传递的修改状态的方法

3.item孙组件中触发需要修改状态的事件源,在事件中调用props中传递过来的方法(定义在爷组件中),并传递修改的标识参数方便App组件中的方法修改状态使用。

代码:

App.jsx

javascript 复制代码
import React, { Component } from 'react'
import Header from './components/Header';
import List from './components/List';
import Footer from './components/Footer';
import './App.css';

class App extends Component {
  state={todos:[
    {id:'001',name:'吃饭',done:true},
    {id:'002',name:'睡觉',done:true},
    {id:'003',name:'打代码',done:false},
  ]}
   a=(info)=>{
     const{todos}=this.state
     const newTodos=[info,...todos]
     console.log('this',this)
    //  修改状态
     this.setState({todos:newTodos})
  }
//   修改todos中的done
  updateTodo=(id,done)=>{
    const {todos}=this.state
    // 匹配处理数据
    const newTodos=todos.map((todoObj)=>{
        if(todoObj.id===id) return {...todoObj,done}
        else return todoObj
    })
    // 修改状态
    this.setState({todos:newTodos})
  }
 render(){
    return (
        <div className="App">
          <div className="todo-container">
            <div className="todo-wrap">
             <Header a={this.a}/>
             <List {...this.state} updateTodo={this.updateTodo}/>
             <Footer/>
          </div>
         </div>
        </div>
      );
 }
}

export default App;

list.jsx

javascript 复制代码
  render() {
    console.log('list',this.props)
    const {todos,updateTodo}=this.props
    return (
      <div>
         <ul className="todo-main">
            {todos.map((item)=>(   <Item  key={item.id} {...item} updateTodo={updateTodo} />) )}
         </ul>
      </div>
    )
  }

Item.jsx

javascript 复制代码
import React, { Component } from 'react'
import './index.css'

export default class Item extends Component {
  
    state={mouse:false}
   handleMouse=(flag)=>{
     return ()=>{
        console.log(flag)
        this.setState({mouse:flag})
        console.log('mouse',this.state.mouse)
     }
  }
//  修改App组件中todos列表数据中的done
 changeHandle=(event)=>{
    const {id,updateTodo}=this.props
    console.log(event.target.checked)
    updateTodo(id,event.target.checked)
  }
  render() {
    const {name,done}=this.props
    const {mouse}=this.state
    return (
      <div>
        {/* defaultChecked只管一上来是不是勾选,如果是checked则是后续不能修改 */}
       {
        <li style={{backgroundColor:mouse?'#ddd':'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
          <label>
            <input type="checkbox" defaultChecked={done} onChange={this.changeHandle}/>
            <span>{name}</span>
          </label>
          <button className="btn btn-danger" style={{display:mouse?'block':'none'}}>{'删除'}</button>
        </li>
       }
        
      </div>
    )
  }
}
四、对props进行类型限制

安装库:npm i prop-types

引用:import PropTypes from "prop-types"

五、实现删除功能

1、状态所在组件中绑定删除事件,并实现删除逻辑

2、子组件或孙组件中触发事件源并调用app组件中修改状态的方法。

六、实现底部全选功能和删除已完成任务

代码:

javascript 复制代码
import React, { Component } from 'react'
import Header from './components/Header';
import List from './components/List';
import Footer from './components/Footer';

import './App.css';

class App extends Component {
  state={todos:[
    {id:'001',name:'吃饭',done:true},
    {id:'002',name:'睡觉',done:true},
    {id:'003',name:'打代码',done:false},
  ]}
   a=(info)=>{
     const{todos}=this.state
     const newTodos=[info,...todos]
     console.log('this',this)
    //  修改状态
     this.setState({todos:newTodos})
  }
//   修改todos中的done
  updateTodo=(id,done)=>{
    const {todos}=this.state
    // 匹配处理数据
    const newTodos=todos.map((todoObj)=>{
        if(todoObj.id===id) return {...todoObj,done}
        else return todoObj
    })
    // 修改状态
    this.setState({todos:newTodos})
  }
 //  删除todo
  deleteTodo=(id)=>{
    const {todos}=this.state
    if(window.confirm('您确定删除吗?')){
        const newTodos=todos.filter(item=>item.id!==id)
        this.setState({todos:newTodos})
    }
   
  }
//   全选按钮
  checkAllTodo=(done)=>{
    const {todos}=this.state
    const newTodos=todos.map(todo=>{return {...todo,done}})
    this.setState({todos:newTodos})
  }
//   删除已完成任务
  deleteDone=()=>{
    const {todos}=this.state
    const newTodos=todos.filter(todo=>{
        return !todo.done
    })
    this.setState({todos:newTodos})
  }
 render(){
    return (
        <div className="App">
          <div className="todo-container">
            <div className="todo-wrap">
             <Header a={this.a}/>
             <List {...this.state} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/>
             <Footer {...this.state} checkAllTodo={this.checkAllTodo} deleteDone={this.deleteDone}/>
          </div>
         </div>
        </div>
      );
 }
}

export default App;

footer:

javascript 复制代码
import React, { Component } from 'react'
import './index.css'

export default class Footer extends Component {
    // 全选按钮
    changeAll=(event)=>{
     
      this.props.checkAllTodo(event.target.checked)
    }
    // 删除已完成
    deleteDone=()=>{
        this.props.deleteDone()
    }
  render() {
    const {todos}=this.props
    const total=todos.length
    const checkedCount=todos.reduce((pre,cur)=>{return pre+=cur.done?1:0},0)
    return (
      <div>
         <div className="todo-footer">
           <label>
            <input type="checkbox" checked={total===checkedCount&&total>0?true:false} onChange={this.changeAll}/>
          </label>
          <span>
            <span>已完成{checkedCount}</span> / 全部{total}
          </span>
         <button className="btn btn-danger" onClick={this.deleteDone}>清除已完成任务</button>
      </div>
      </div>
    )
  }
}
七、总结案例:

1、拆分组件、实现静态组件,注意:className,style的写法

2、动态初始化列表,如何确定将数据放在那个组件的state中?

-----某个组件使用:放在自身的state中

-----某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)

3.关于父子之间通信:

1.【父组件】给【子组件】传递数据:通过props传递

2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数

4.注意defaultChecked和checked的区别,类似的还有:defaultValue和value

5.状态在哪里,操作状态的方法就在哪里

相关推荐
baiduopenmap1 分钟前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
请叫我欧皇i21 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_24 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
guokanglun30 分钟前
空间数据存储格式GeoJSON
前端
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium
猫爪笔记1 小时前
前端:HTML (学习笔记)【2】
前端·笔记·学习·html
brief of gali1 小时前
记录一个奇怪的前端布局现象
前端
Json_181790144802 小时前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
风尚云网3 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
木子02043 小时前
前端VUE项目启动方式
前端·javascript·vue.js