1.目标
A. 能够知道setState()更新数据是异步的
B. 能够知道JSX语法的转化过程
C. 能够说出React组件的更新机制
D. 能够对组件进行性能优化
E. 能够说出虚拟DOM和Diff算法
2.目录
A. setState()的说明
B. JSX语法的转化过程
C. 组件更新机制
D. 组件性能优化
E. 虚拟DOM和Diff算法
3.setState()的说明
3.1 更新数据
A. setState() 是异步更新数据的
B. 注意:使用该语法时,后面的setState()不能依赖于前面的setState()
C. 可以多次调用setState(),只会触发一次重新渲染1setState.js
import React from "react";
class App31 extends React.Component {
state = {
count: 0,
};
handleClick = () => {
//异步更新操作
this.setState({
count: this.state.count + 1,
});
console.log("count1:" + this.state.count);
this.setState({
count: this.state.count + 1,
});
console.log("count2:" + this.state.count);
};
render() {
//数据更新了就会调用一次render
//但是,如果数据变化一样的,render只调用一次
console.log("render");
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>点击</button>
</div>
);
}
}
export default App31;
index,js
import App31 from "./1setState";
ReactDOM.createRoot(document.getElementById("root")).render(<App31></App31>);
3.2 推荐语法
A. 推荐:使用setState((state,props)=>{})语法
B. 参数state:表示最新的state
C. 参数props:表示最新的props
import React from "react";
class App31 extends React.Component {
state = {
count: 1,
};
handleClick = () => {
// //异步更新操作
// this.setState({
// count: this.state.count + 1,
// });
// console.log("count1:" + this.state.count);
// this.setState({
// count: this.state.count + 1,
// });
// console.log("count2:" + this.state.count);
//推荐语法
//注意:这种语法也是异步更新state的,但是会记录最新的state的数据,所以在页面显示为3
this.setState((state, props) => {
console.log("state1:", state, "props1:", props);
return {
count: state.count + 1,
};
});
console.log("count:", this.state.count); // 1
this.setState((state, props) => {
console.log("state2:", state, "props2:", props);
return {
count: state.count + 1,
};
});
console.log("count:", this.state.count); // 1
};
render() {
//数据更新了就会调用一次render
//但是,如果数据变化一样的,render只调用一次
console.log("render");
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>点击</button>
</div>
);
}
}
export default App31;
3.3 第二个参数
A. 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
B. 语法:setState(update [,callback])
import React from "react";
class App31 extends React.Component {
state = {
count: 1,
};
handleClick = () => {
// //异步更新操作
// this.setState({
// count: this.state.count + 1,
// });
// console.log("count1:" + this.state.count);
// this.setState({
// count: this.state.count + 1,
// });
// console.log("count2:" + this.state.count);
//推荐语法
//注意:这种语法也是异步更新state的,但是会记录最新的state的数据,所以在页面显示为3
// this.setState((state, props) => {
// console.log("state1:", state, "props1:", props);
// return {
// count: state.count + 1,
// };
// });
// console.log("count:", this.state.count); // 1
// this.setState((state, props) => {
// console.log("state2:", state, "props2:", props);
// return {
// count: state.count + 1,
// };
// });
// console.log("count:", this.state.count); // 1
// 第二个参数,在状态更新(页面完成重新渲染)后立即执行某个操作
this.setState(
(state, props) => {
return {
count: state.count + 1,
};
},
() => {
console.log("状态更新完成,当前count值:", this.state.count);
}
);
};
render() {
//数据更新了就会调用一次render
//但是,如果数据变化一样的,render只调用一次
console.log("render");
return (
<div>
<h1>计数器:{this.state.count}</h1>
<button onClick={this.handleClick}>点击</button>
</div>
);
}
}
export default App31;
4.JSX语法的转化过程
A. JSX仅仅是createElement()方法的语法糖(简化语法)
B. JSX语法被@babel/preset-react插件编译为createElement()方法
C. React元素:是一个对象,用来描述你希望的屏幕上看到的内容
const element=<h1 className='greeting'>Hello JSX!</h1>
// const element1=React.createElement('h1',{
// className:'greeting'
// },'Hello JSX!!!')
console.log(element);
// console.log(element1);
ReactDOM.render(element,document.getElementById('root'))
效果图:
5.组件更新机制
A. setState()的两个作用:1.修改state 2.更新组件(UI)
B. 过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
首次加载时渲染
点击根组件会触发所有组件,点击左侧父组件1时会触发局部更新,只更新当前组件与子组件,不会触发父组件2comUpdate.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./comUpdate.css";
class App50 extends React.Component {
state = {
bgColor: "#369",
};
getBgColor = () => {
return Math.floor(Math.random() * 256);
};
toggleBgColor = () => {
this.setState({
bgColor: `rgb(${this.getBgColor()},${this.getBgColor()},${this.getBgColor()})`,
});
};
render() {
console.log("根组件");
return (
<div
className="rootParent"
style={{ backgroundColor: this.state.bgColor }}
>
根组件
<button onClick={this.toggleBgColor}>切换根组件颜色</button>
<div className="app-wrapper">
<Parent1></Parent1>
<Parent2></Parent2>
</div>
</div>
);
}
}
// 左边
class Parent1 extends React.Component {
state = {
count: 0,
};
handleClick = (state) => {
this.setState((state) => {
return {
count: state.count + 1,
};
});
};
render() {
console.log("左侧父组件 1");
return (
<div className="Parent1">
<span>左侧-父组件</span>
<button onClick={this.handleClick}>点击({this.state.count})</button>
<div className="parentWrapper1">
<Child1></Child1>
<Child2></Child2>
</div>
</div>
);
}
}
class Child1 extends React.Component {
render() {
console.log("左侧子组件 1-1");
return <div className="child1">左侧子组件1-1</div>;
}
}
class Child2 extends React.Component {
render() {
console.log("左侧子组件 1-2");
return <div className="child2">左侧子组件1-2</div>;
}
}
// 右边
class Parent2 extends React.Component {
state = {
count: 0,
};
handleClick = (state) => {
this.setState((state) => {
return {
count: state.count + 2,
};
});
};
render() {
console.log("右侧父组件 2");
return (
<div className="Parent1">
<span>右侧-父组件</span>
<button onClick={this.handleClick}>点击({this.state.count})</button>
<div className="parentWrapper1">
<Child3></Child3>
<Child4></Child4>
</div>
</div>
);
}
}
class Child3 extends React.Component {
render() {
console.log("右侧子组件 2-1");
return <div className="child1">右侧子组件2-1</div>;
}
}
class Child4 extends React.Component {
render() {
console.log("右侧子组件 2-2");
return <div className="child2">右侧子组件2-2</div>;
}
}
export default App50;
index.js
import App50 from "./2comUpdate";
ReactDOM.createRoot(document.getElementById("root")).render(<App50></App50>);
comUpdate.css
.rootParent {
width: 800px;
height: 600px;
margin: 0 auto;
font-weight: 700;
font-size: 22px;
}
.app-wrapper {
display: flex;
justify-content: space-between;
margin-top: 20px;
}
.Parent1 {
width: 40%;
height: 400px;
background-color: pink;
}
.parentWrapper1 {
display: flex;
justify-content: space-between;
margin-top: 30px;
}
.child1 {
width: 40%;
height: 300px;
background-color: lightgreen;
}
.child2 {
width: 40%;
height: 300px;
background-color: lightgrey;
}
.Parent2 {
width: 40%;
height: 400px;
background-color: salmon;
}
6.组件性能优化
6.1 减轻state
A. 减轻state:只存储跟组件渲染相关的数据(比如:count/列表数据/loading等)
B. 注意:不用做渲染的数据不要放在state中,比如定时器id等
C. 对于这种需要在多个方法中用到的数据,应该放在this中
6.2 避免不要的重新渲染
A. 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
B. 问题:子组件没有任何变化时也会重新渲染
C. 如何避免不必要的重新渲染吗?
D. 解决方案:使用钩子函数shouldComponentUpdate(nextProps,nextState)
E. 作用:通过返回值决定该组件是否重新渲染,返回true表示重新渲染,false表示不重新渲染
F. 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate->render)3performanceOptimize.js
import React from "react";
class App62 extends React.Component {
state = {
count: 0,
};
handleClick = () => {
this.setState((state) => {
return {
count: state.count + 1,
};
});
};
//钩子函数
shouldComponentUpdate(nextProps, nextState) {
//返回false,阻止组件重新渲染
// return false;
//最新的状态
console.log("最新的state:", nextState);
//更新前的状态
console.log("this.state:", this.state);
return true;
}
render() {
console.log("组件更新了");
return (
<div>
<h1>count:{this.state.count}</h1>
<button onClick={this.handleClick}>点击</button>
</div>
);
}
}
export default App62;
index.js
import App62 from "./3performanceOptimize";
ReactDOM.createRoot(document.getElementById("root")).render(<App62></App62>);
6.2.1 案例:随机数(nextState)
4App621Random.js
import React from "react";
class App621Random extends React.Component {
state = {
number: 0,
};
getRandom = () => {
this.setState((state) => {
return {
number: Math.floor(Math.random() * 3),
};
});
};
//因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染
shouldComponentUpdate(nextProps, nextState) {
if (nextState.number === this.state.number) {
return false;
}
return true;
}
render() {
console.log("触发渲染");
return (
<div>
<h1>随机数:{this.state.number}</h1><