1- props属性
1-1 可以传递数据
父组件通过自定义属性可以传递数据,子组件通过this.props接收
---父组件
render() { return ( <div className='box'> <h1>father组件</h1> <h2>自己的数据</h2> {/* 自定属性传递数据 */} <Son name={this.state.name} age={this.state.age}></Son> </div> ) }
--子组件
render() { console.log(this.props); return ( <div className='box'> <h1>son组件---</h1> <h2>父组件传递的数据---{this.props.name}</h2> </div> ) }
1-2 可以批量传值
{/* 单个的传值 */} {/* <SonSon name={this.props.name} age={this.props.age}></SonSon> */} {/* 批量传值 */} <SonSon {...this.props}></SonSon>
1-3 在constructor中使用props
constructor(props){ // 1.主要是为了继承, 2. 只有调用以后,才可以使用this super(props) // 想访问props数据,不可以直接this.props获取,需要先接收 console.log(this.props); }
1-4 props可以传递接收DOM结构
---父组件传递
{/* 自定属性传递数据 */} <Son name={this.state.name} age={this.state.age}> {/* 传递dom结构 */} <h1>携带的结构</h1> </Son>
---子组件接收,this.props.children 接收传递的dom结构
<h1>son组件---</h1> {/* this.props 中children存的是dom节点 */} {this.props.children} <h2>父组件传递的数据---{this.props.name}</h2>
1-5 传递方法
changeName(){ this.setState({ name:'小黑' }) } render() { return ( <div className='box'> <h1>father组件</h1> <h2>自己的数据</h2> {/* 自定属性传递数据 */} {/* 自定属性传递方法 */} <Son name={this.state.name} age={this.state.age} changeName={()=>this.changeName()}> {/* 传递dom结构 */} <h1>携带的结构</h1> </Son> </div> ) }
--子组件
<h1>son组件---</h1> {/* props调用传递的方法 */} <button onClick={this.props.changeName}>修改name</button>
1-6 props和state的区别
-
state中存的是组件自己的数据.props数据是父组件传递的数据
-
state的数据是可读可写,props只具有可读性
2- 组件通讯
2.1- 父子组件通讯
-
父组件通过自定义属性传值
-
子组件通过this.props接收数据
export default class Father extends Component { state={ val:'父组件的数据' } render() { let {val} =this.state return ( <div className='box'> <h1>父组件</h1> <h2>自己的数据---{val}</h2> {/* 自定义属性传递 */} <Son val={val}></Son> </div> ) } }
---子组件
export default class Son extends Component { render() { // 通过props接收传递的数据 console.log(this.props); // 解构 let {val} =this.props return ( <div className='box'> <h1>子组件</h1> <h2>父组件传递的数据--{val}</h2> </div> ) } }
2.2- 子父组件通讯
-
利用组定义属性传递方法实现传值
-
父组件调用子组件时通过自定义属性传递方法
-
子组件接收到这个方法,在调用方法时可以传递参数(需要传递的数据)
---父组件
export default class Father extends Component { state={ val:'父组件的数据', data:'' //接收子组件数据 } // 接收子组件数据的方法 rev(val){ console.log(val); this.setState({ data:val }) } render() { let {val,data} =this.state return ( <div className='box'> <h1>父组件</h1> <h2>自己的数据---{val}</h2> <h2>子组件的数据===={data}</h2> {/* 自定义属性传递 */} {/* 自定义属性传递方法 (形参)=>this.rev(实参) */} <Son val={val} rev={(val)=>this.rev(val)}></Son> </div> ) } }
---子组件
export default class Son extends Component { state={ age:99 } render() { // 通过props接收传递的数据 console.log(this.props); // 解构 let {val,rev} =this.props // let {age} = this.state return ( <div className='box'> <h1>子组件</h1> <h2>父组件传递的数据--{val}</h2> <button onClick={()=>rev(this.state.age)}>传值</button> </div> ) } }
3- 生命周期
/** * 1. 初始阶段 * - constructor 初始化状态 * - getDerivedStateFromProps 在正式渲染之前执行,渲染之前可以再次修改state * 注意: * 1. 必须是static静态方法 * 2. 必须要有返回值, 如果修改了state,返回修改之后的,如果不需要修改,return null * 3. 必须是已经初始化过state * - render 视图渲染,加载DOM节点 * return值是需要渲染的结构 * - componentDidMount 挂载完成,渲染完毕.发起请求,开启定时器.... * * 2. 运行阶段(更新阶段) * - getDerivedStateFormProps 渲染之前,可以再次修改state * - shouldComponentUpdate 询问是否要重新渲染 * 必须有返回值 true 继续更新渲染; false 不更新 * //第一个形参: 跟新之后,下一次的props数据对象 // 第二个形参: 更新之后,下一次的state对象 * * - render 更新渲染 * - componentDidUpdate 更新完成 * * 3.销毁阶段 * - componentWillUnmount 销毁之前,关闭定时器,挂载在全局的变量 * * */
4- ref
作用:注册引用信息
-
1-创建ref对象
// 1- 创建一个ref对象 one = React.createRef()
-
2-将创建的ref对象通过ref绑定在DOM或者组件上
<h1 ref={this.one}>ref组件</h1>
-
3-ref对象代表的就是DOM或者组件对象
console.log(this.one);
注册在普通节点时获取的就是DOM节点
注册在组件上时获取的组件对象
5- 表单
5.1- 非受控组件
-
表单元素的value和state的数据任何关系
-
就是普通的表单,只有现在提交时获取一次input框的值
-
只能读取值,不能设置值,没有value
<div> <h1>非受控组件</h1> <div> 用户名: <input ref={this.userRef} type="text" /> <br /> 内容: <input ref={this.txtRef} type="text" /> <br /> <button onClick={() => this.sub()}>提交</button> </div> sub() { // 通过ref获取到dom节点,从而获取value值 console.log(this.txtRef.current.value); console.log(this.userRef.current.value); }
5.2-受控组件
-
表单元素的value和state中的某个数据绑定
-
必须绑定onChange事件,
export default class Control extends Component { state={ username:'jack', password:'', phone:'' } // 修改username changeUser(e){ let val = null //输入框的值 // 获取用户输入的内容 val=e.target.value console.log(val); // 修改username this.setState({ username:val }) } // 同步pasdword changePassword(e){ let val=null //输入框数据 val = e.target.value this.setState({ password:val }) } // 表单同步 change(e,key){ let val = null //输入框的数据 val = e.target.value this.setState({ [key]:val }) } render() { let {username,password,phone} =this.state return ( <div> <h1>受控组件</h1> {/* 用户名:<input type="text" value={username} onChange={(e)=>this.changeUser(e)} /> */} 用户名:<input type="text" value={username} onChange={(e)=>this.change(e,'username')} /> {username} <br /> {/* 密码: <input type="text" value={password} onChange={(e)=>this.changePassword(e)} /> */} 密码: <input type="text" value={password} onChange={(e)=>this.change(e,'password')} /> {password} <br /> 手机号: <input type="text" value={phone} onChange={(e)=>this.change(e,'phone')} /> {phone} </div> ) } }
5.3、受控和非受控组件对比
特性 | 受控组件 | 非受控组件 |
---|---|---|
一次性索引(例如表单提交获取数据) | 是 | 是 |
及时验证 | 是 | 否 |
有条件的禁用提交按钮 | 是 | 否 |
执行输入格式 | 是 | 否 |
一个数据的几个输入 | 是 | 否 |
动态绑定 | 是 | 否 |
5.4 完整表单
import React, { Component } from 'react' export default class Case extends Component { /** * 1. 用户的所有信息 * username: 用户名 * phone: 手机号 * password 密码 * sex: 性别 1---男 0---女 * hobby: 爱好 ['篮球','排球','足球','羽毛球'] * job: 职业 web工程师---web,java工程师---java,python工程师---python * des: 个人描述 */ state={ obj:{ username:'nick', phone:'110', password:'123', sex:'0', hobby:['篮球','足球'], job:'python', des:'12324' } } // 表单数据同步 change(e,key){ let val =null //输入框的数据 val = e.target.value // 如果是多选框需要追加value,而不是替换之前的值 if(key=='hobby'){ // 获取之前的数组, let arr = this.state.obj.hobby // 需要根据当前输入框是选中还是取消,来决定是追加还是删除数据 // console.log(e.target.checked); if(e.target.checked){ // 选中 arr.push(val) }else{ // 取消 arr.splice(arr.indexOf(val),1) } val=arr } this.setState({ obj:{ ...this.state.obj, [key]:val } }) } sub(){ console.log(this.state.obj); } render() { let {obj} =this.state return ( <div> <h1>完整的表单绑定</h1> <p>用户名: <input type="text" value={obj.username} onChange={(e)=>this.change(e,'username')} /> {obj.username} </p> <p>手机号: <input type="text" value={obj.phone} onChange={(e)=>this.change(e,'phone')} /> {obj.phone} </p> <p>密码: <input type="text" value={obj.password} onChange={(e)=>this.change(e,'password')} /> {obj.password} </p> <p>性别: <input type="radio" name='sex' checked={obj.sex=='1'?true:false} value='1' onChange={(e)=>this.change(e,'sex')} />男 <input type="radio" name='sex' checked={obj.sex=='0'?true:false} value='0' onChange={(e)=>this.change(e,'sex')} />女 {obj.sex} </p> <p> 爱好: <input checked={obj.hobby.includes('篮球')} onChange={(e)=>this.change(e,'hobby')} type="checkbox" name='hobby' value='篮球' />篮球 <input checked={obj.hobby.includes('足球')} onChange={(e)=>this.change(e,'hobby')} type="checkbox" name='hobby' value='足球' />足球 <input checked={obj.hobby.includes('排球')} onChange={(e)=>this.change(e,'hobby')} type="checkbox" name='hobby' value='排球' />排球 <input checked={obj.hobby.includes('羽毛球')} onChange={(e)=>this.change(e,'hobby')} type="checkbox" name='hobby' value='羽毛球' />羽毛球 {JSON.stringify(obj.hobby)} </p> <p>职业 <select value={obj.job} onChange={(e)=>this.change(e,'job')}> <option value="web">web工程师</option> <option value="java">java工程师</option> <option value="python">python工程师</option> </select> {obj.job} </p> <p> 个人描述: <textarea value={obj.des} onChange={(e)=>this.change(e,'des')} cols="30" rows="10"></textarea> {obj.des} </p> <button onClick={()=>this.sub()}>提交</button> </div> ) } }
6- 组件优化
1- componentWillUnmount
场景: 如果组件运行时开启了定时器,就需要在组件将要销毁时清除定时器
import React, { Component } from 'react' export default class Son extends Component { state={ time:new Date() } // 挂载完成 componentDidMount(){ this.timer = setInterval(()=>{ this.setState({ time:new Date() }) },1000) } componentWillUnmount(){ //清除定时器 clearInterval(this.timer) } render() { return ( <div className='box'> <h1>子组件</h1> <h2>{this.state.time.toLocaleTimeString()}</h2> </div> ) } }
2- shouldComponentUpdate
场景: 父组件向子组件传值, 父组件改变数据时一定会引入子组件的重新渲染.
如果传递给子组件的数据没有变化,这时子组件不需要重新渲染.可以shouldComponentUpdate判断是否要重新渲染
shouldComponentUpdate(nextProps,nextState){ // 父组件的age如果没有改变,子组件就不重新渲染 if(nextProps.age===this.props.age){ return false }else{ return true } }
3- PureComponent
场景: 和2 场景一样. 使用PureComponent 在重新渲染之前会将数据做一个浅比较.如果所有的数据都没有改变,不会触发重新渲染.只要有一个改变就会重新渲染
import React, { Component,PureComponent } from 'react' //继承 PureComponent export default class Son extends PureComponent { state={ } // 挂载完成 componentDidMount(){ } componentWillUnmount(){ } render() { console.log('son组件的render'); return ( <div className='box'> <h1>子组件</h1> <h2>传递的age---{this.props.age}---{this.props.num}</h2> </div> ) } }
4- React.memo()
场景: 和场景2,3一样,函数组件没有声明周期,没有继承PureComponent,但是可以通过memo处理
import React from 'react' //函数组件的props是通过形参接收 function Son(props) { console.log('子组件渲染'); return ( <div className='box'> <h1>子组件</h1> <h2>父组件传递的数据 ---{props.age}</h2> </div> ) } export default React.memo(Son)