案例效果:
一、App组件和header输入框的交互:
需求:header输入框中输入活动项,回车之后添加在app组件的state状态中
实现:1、通过在App父组件中添加一个事件,子组件中可以通过this.props.a的方法触发;
2、子组件中调用方法并通过参数传递需要增加的数据
(1).App组件
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组件
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> ) } }
二、实现鼠标移入移出展示效果
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
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
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
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组件中修改状态的方法。
六、实现底部全选功能和删除已完成任务
代码:
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:
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.状态在哪里,操作状态的方法就在哪里