02 - react State异步问题、props数据校验、生命周期
笔记批注:易错 参考文献 重点 面试题
01 props
01.01 父子组件传值(插槽使用,类型控制)
- 插槽,props数据传值;
- props 控制数据类型;
2.1 法一:配置静态属性;
2.2 法二:配置类属性;
-
Button.js
import React, {Component} from "react"; class Button extends Component { render() { return { console.log(this.props) <button>{this.props.children}</buttton> // 打印出来是“增加”; } } } export default Button -
Child.jsx
import React, {Component} from "react"; import Button from "./Button" import types from "prop-types"//2.1 class Child extends Component { render(){ /** * 2. 配置校验规则: * 变量必须静态属性 -- static -- 不能在实例上调用 * 属性名字与state一样固定不能够修改; */ static propTypes = { // count: String 不支持; count: types.number.isRequired //2.2 必须先类型再是否必传;类型错误,必传效果都会检验报错; myAddEvt: types.func } addEvt(step){ this.props.myAddEvt(5); } return { <div> Child.Component... <p> 父组件的count: {this.props.count} <Button>{} 增加</Button> // 1. 没有插槽,放在Button(子组件)内部的文字,使用Children传递给子组件; </p> </div> } } } // 2.3 通过类属性配置校验规则; Child.propTypes = { // count: types.number.isRequired //2.2 必须先类型再是否必传;类型错误,必传效果都会检验报错; myAddEvt: types.func } // 2.3.1 设置默认值 Child.defaultProps = { count: 10000 } export default Child -
App.jsx
import React, {Component} from "react"; import Child from "./Child" class App from extends Component { state = { count: 10 } addEvt = frunction(step){ this.setState({ count: this.state.count + step; }) } render(){ return( <div> App Page... <hr> // 1. 需要的字段都传递了; <Child count={this.state.count} myAddEvt={this.addEvt.bind(this)}> <hr> // 2. 需要的字段没有完全传递; // 2.1 count传递的是字符串 ==》 报错; // 2.2 myAddEvt传递的是数字不是函数 ==》 报错; <Child count="12" myAddEvt={ 23 }/> </div> ) } }
- 插槽:在react里面没有插槽的说法;react中组件中传递数据,子组件中使用children来接收 //this.props.children;
- props控制数据类型:
- 数据类型校验: npm.js库中 prop-types
- 安装命令:yarn add props-types;
01.02 状态提升解读
- A的数据B用;A传给App.jsx, App.jsx传给B;
- 变量提升:变量其他组件使用,就不要定义再这个组件内部;
- 变量this指向;
- 受控组件:组件内部的所有属性都不定义在自己内部;
- 受控表单:组件内部,没有自己的state数据,也没有自己修改数据的方法;
-
PageA.jsx
import React. {Component} from "react" export default PageA from Component { // state = { // count: 10 // } render() { return ( <div> PageA page... {this.state.count} <input type="text" value={this.props.count} onChange={(evt) => { console.log(evt.target.value) // evt 这里拿到的是字符串,因此在iptEvt setState 中必须转为数据类型; this.props.inputEvt(evt.target.value) //调用this上 }}> // 4.0 受控表单 </div> ) } } -
PageB.jsx
import React. {Component} from "react" export default PageB from Component { state = { } render() { return ( <div> PageB page... {this.props.count} <button onClick={ () => { this.props.munisEvt(2); } }>减少</button> // 1.3 合成事件中可以直接编写方法; </div> ) } } -
App.jsx
import React , {Component} from "react" import PageA from "./PageA" import PageB from "./PageB" class App extends Component { render() { state = { count: 1000 // 1.1 } minusEvt(step){ this.setState({ count: this.state.count - step; }) } iptEvt(val) { this.setState({ count: val *1; // 转换方式1; count: +val // 隐式转换方法; 类似于+ new Date() }) } return { <div> App.page... <hr> <PageA count={this.state.count} iptEvt={this.iptEvt.bind(this)}/> //将iptEvt 绑定上去; <PageB count={this.state.count} munisEvt={this.munusEvt.bind(this)}/>// 1.2 变量提升; </div> } } }
01.03 基础生命周期(主要面试,实际开发使用少)
- 生命周期
- 作用: 在一个组件从创建到销毁的特殊环节暴露出来的钩子函数,允许我们往钩子函数中添加业务。
- 应用:组件初始化的时候,要获取数据,那么可以在创建生命周期来调用获取数据的接口。
- 对比vue:vue18个生命周期,react按照js执行顺序定义生命周期。
- 独有:只有类组件有生命周期,函数组件没有生命周期。
- 阶段:react生命周期只有三个阶段,业务完成后执行下一次任务。挂载,更新,卸载
- 挂载:创建开始 -> 挂载完成 ; constructor -> componentdisMount
- 参考文献:https://react.dev/reference/react/Component
App.jsx
import React,{Component} from "react"
export default class App extends Component{
render(){
constructor(){
// 1.1 构造器就是第一个生命周期;
this.state = {}
}
static getDerivedStateFromProps(_newProps,_state) {//静态方法没有this; 没有new运算;
console.log(arguments)
// 1.2.3 react组件更新有可能是state数据变化,props变化,生命周期等价于挂载和更新生命周期;
// 1.2 第二个生命周期:生命周期会将数据对象合并到state对象里,log的this打印出来为state.count
// 1.2.1 应用场景:props数据不可以更新,但同时不会影响父组件。
// 1.2.2 一旦使用该生命周期必须定义state数据;
return {
count: 10//1.2.2;
count: _newProps.count// 1.2.3
}
// 1.2.3 react生命周期getDerivedStateFromProps需要判断具体在哪个状态,否则不生效;
return _state.count ? null : {
count: _newProps.count
}
}
console.log(this,arguments)
// 1.3 第五个生命周期;类似于vue的mounted和onMounted生命周期;
componentDidMount(){
console.log("五,前面三四不常用");
// 1.3.1 react 挂载生命周期阶段,只有componentDisMount可以执行setState方法;
}
// 1.4 第六个生命周期;更新完成生命周期;
conmponentDisUpdate(_newProps, _oldState){ // 1.4.1 state数据是更新前的;现在的state没有;可以用this.state.count替代;
console.log("更新完成生命周期");
console.log(arguments)
}
return (
<div>
App Page... { this.props.count },{ this.state.count}//1.2.2此时两个值分别为1000,10;
App Page... { this.props.count },{ this.state.count}//1.2.3此时两个值分别为1000,1000;
// 1.2.4此时添加按钮,操作count;this.props.count不会修改,this.state.count就是获取到的衍生数据会被修改;
<button onClick={this.state.count ++ }>增加</button> //可能有问题
// 1.3.1 render方法不能执行setState方法,因为setState会调用render,形成递归;setState调用render,render调用state;
// button上面调用方法;
<button onClick={() => { this.setstate({
count: this.state.count +1
})
}}>
</div>
)
}
}
index.js
import React,{Component} from "react";
import App from "./App.jsx";
const root = React.CreateRoot(document.getElementById("root"));
root.render(
<App count = {1000} />
)
面试题: Object.assign 是浅拷贝, 数据对象只有一层的时候是深拷贝;
- 卸载生命周期;
- 唯一可以使用的will的生命周期,react官网可查(其他的基本不使用UNSave)
- componentWillUnmount()
Child.jsx
import React,{Component} from "react";
export default class Child extends Component{
state = {
timer: 0;
}
componentDidMount(){
let timer = setInterVal(function(){
console.log('1')
},1000)
this.setState({timer }) // 单项数据流,设置比较特殊不同于vue;
};
componentWillUnmouont(){
clearInterval(this.state.timer) // 离开页面的时候销毁定时器;
};
render(){
return(
<div>
Child.jsx组件;
<hr/>
<p>我的组件卸载;</p>
</div>
)
}
}
App.jsx
//1. 父组件;
import React , {Component} from "react"
import Child from "./Child.jsx"
export default class App extends Component {
constructor(){
show:
}
render () {
// 1.1 卸载组件(条件渲染);
return(
<div>
App Page..
<button class="toggle" onClick={
() => {
this.setState({show: !this.state.show}) // 1.3 变量的修改方法;setState的使用;
}
}>切换</button>
{
this.state.show && <Child> // 1.2 并且运算符,前面为真继续向后运算,否则直接停止;
}
</div>
)
}
}
- 渲染优化生命周期;
App.jsx
import React, {Component} from "react";
export default class App extends Component{
state = {
list: Array.from(Array(6),(_,i) =>({
id: "id"-i,
name: "化为手机",
price: 4000,
count: 1
}))
};
addEvt(id,step){
this.setState({
list: this.state.list.map(it => {
if(it.id === id) it.count += step
return it;
})
})
};
render(){
return (
<div>
<table className="table">
<thead>
<tr>
<th>商品名字</th>
<th>商品价格</th>
<th>商品数量</th>
</tr>
</thead>
<tbody>
{this.state.list.map((item) => {
return (
<tr key={it.id}> // 不要使用下标作为key;
<td>{it.name}</td>
<td>{it.price}</td>
<td>
<button onClick={this.addEvt.bind(this,it.id,2)}>+</button>
{it.count}
<button>-</button>
</td>
<td></td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}
}
item.jsx
import React,{Component} from "react";
重点: key不要使用index,原理:数组若存在增加删除等排序操作,key的顺序就会乱,失去了作用;
- 直接将render中的list组件遍历在App.jsx 中,每次修改一个数量,整个组件都会重新渲染,将其单独抽出新的组件Item;

浙公网安备 33010602011771号