详解setState
1.开发中我们并不能直接通过修改state的值来让界面发生更新:
- 因为我们修改了state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化;
- React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化;
- 我们必须通过setState来告知React数据E经发生了变化;
2.setState函数是我们继承的Component原型中定义的。
3.setState是异步更新的
- 我们不能在执行完setState之后立马拿到最新的state的结果。
export default class App extends Component {
constructor(props){
super(props);
this.state = {
message:'setState的更新方式'
}
}
render() {
return (
<div>
<h2>setState的更新方式</h2>
<button onClick={e => this.changeText()}>修改setstate</button>
</div>
)
}
changeText(){
this.setState({
message:'setState是异步更新的'
})
console.log(this.state.message) //setState的更新方式
}
}
- react异步更新的好处
- setState设计为异步,可以显著的提升性能;如果每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面
重新渲染,这样效率是很低的;最好的办法应该是获取到多个更新,之后进行批量更新; - 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步;state和props不能保持一致性,会在开发中产生很多的问题;
- setState设计为异步,可以显著的提升性能;如果每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面
4.那么如何获取到异步更新后的数据呢?
- 方式1:
/**
* 方式1:获取异步更新后的数据
* setState可以传入一个回调函数
*/
this.setState({
message:"happy birthday!"
},() => {
console.log(this.state.message)
})
- 方式2:使用生命周期函数
componentDidUpdate(){
console.log(this.state.message)
}
5.两种方式让setState实现同步更新
- 情况1:将setState放进定时器中
/**
* 两种方式让setState实现同步更新
* 方式1:将setState放进定时器中
*/
setTimeout(() => {
this.setState({
message:'happy birthday!'
})
console.log(this.state.message)
})
- 情况2:使用原始的DOM事件监听
componentDidMount(){
document.getElementById('btn').addEventListener('click',()=>{
this.setState({
message:'happy birthday!'
})
console.log(this.state.message)
})
}
6.setState实现了数据合并和自身合并
-
利用了Object.assign() 将两个对象实现了合并。
-
当你调用了三次setState时,内部会发生合并。
详解React更新机制
1.react渲染流程
JSX - 虚拟DOM - 真实DOM
2.React更新流程
props/state改变 -> render函数重新执行 -> 产生新的DOM树
-> 新旧DOM树进行diff算法 -> 计算出差异进行更新 -> 更新到真实的DOM
- props/state改变,会调用React的render方法,创建一颗不同的树
React性能优化
1.react性能优化 — keys
2.react性能优化 — render()
-
shouldComponentUpdata()
/**- 我们需要将组件更新给修改为不调用render函数
- shouldComponentUpdate()
- 返回值为true:那么就需要调用render方法
- 返回值为false:那么就不需要调用render方法
- shouldComponentUpdate()传过来了两个参数
- nextProps, nextState
- nextProps : 最新的props
- nextState : 最新的state
*/
-
PureComponent可以阻止类组件的不必要多次render()更新 PureComponent 中对props和state进行了浅层比较
class Son extends PureComponent{
render(){
console.log('son被调用')
return <h2>我是Son组件</h2>
}
}
export default class App extends PureComponent {
constructor(props){
super(props);
this.state = {
counter:0
}
}
render() {
console.log('App被调用')
return (
<div>
<h2>我是最大的组件</h2>
<Son/>
<h2>当前计数:{this.state.counter}</h2>
<button onClick={ e => this.counterAdd()}>+1</button>
</div>
)
}
counterAdd(){
this.setState({
counter:this.state.counter+1
})
}
changeText(){
this.setState({
message:'你好,中国'
})
}
}
-
但是无论是shouldComponentUpdate() 还是PureComponent都只能组织类组件的render更新
-
那么如何解决函数式组件每次被调用 memo(高阶组件)的使用
memo() 的参数是传入一个函数式组件
import React, { PureComponent, memo } from 'react'
class Son extends PureComponent{
render(){
console.log('son被调用')
return <h2>我是Son组件</h2>
}
}
const MemoSon2 = memo(
function Son2(){
console.log('son2被调用')
return <h2>我是组件son2</h2>
}
)
export default class App extends PureComponent {
constructor(props){
super(props);
this.state = {
counter:0
}
}
render() {
console.log('App被调用')
return (
<div>
<h2>我是最大的组件</h2>
<MemoSon2/>
<Son/>
<h2>当前计数:{this.state.counter}</h2>
<button onClick={ e => this.counterAdd()}>+1</button>
</div>
)
}
counterAdd(){
this.setState({
counter:this.state.counter+1
})
}
changeText(){
this.setState({
message:'你好,中国'
})
}
}