案例效果:
一、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
javascriptimport 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
javascriptrender() { 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
javascriptimport 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组件中修改状态的方法。
六、实现底部全选功能和删除已完成任务
代码:
javascriptimport 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:
javascriptimport 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.状态在哪里,操作状态的方法就在哪里