react的setState

setState

setState是react的异步操作,每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少re-render调用

思考:执行以下这段代码会导致这个组件被重新渲染100次,这对性能是一个非常大的负担,那么react是怎么优化的呢?

for ( let i = 0; i < 100; i++ ) {
  	this.setState( { count: this.state.count + 1 } );
    console.log(this.state.count)
}

React将多个setState的调用合并成一个来执行,这意味着当调用setState时,state并不会立即更新 如下:

class App extends Component {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    componentDidMount() {
        for ( let i = 0; i < 100; i++ ) {
            this.setState( { count: this.state.count + 1 } );
            console.log( this.state.num ); //执行100次打印结果为1,因为setState的延后,不会立即更新
        }
    }
    render() {
    	const {count}=this.state
        return (
            <div>
                <p>{count }</p>
            </div>
        );
    }
}

在这里插入图片描述
组件渲染的结果是1,并且在控制台中输出了100次0,说明每个循环中,拿到的state仍然是更新之前的。
这是React的优化手段,但是显然它也会在导致一些不符合直觉的问题(就如上面这个例子),所以针对这种情况,React给出了一种解决方案:setState接收的参数还可以是一个函数,在这个函数中可以拿先前的状态,并通过这个函数的返回值得到下一个状态。

//更新方法:传一个回调函数
    componentDidMount(){
        for ( let i = 0; i < 100; i++ ) {
            this.setState( prevState => {
                console.log( prevState.count );
                return {
                    count: prevState.count + 1
                }
            } );
        }
    }

渲染结果:
在这里插入图片描述
因此,我们要实现以下两个功能:
1.异步更新state,将短时间内的多个setState合并成一个
2.为了解决异步更新导致的问题,增加另一种形式的setState:接受一个函数作为参数,在函数中可以得到前一个状态并返回下一个状态

合并setState

假设我们不考虑setState方法是异步的,事实上setState方法实现核心是下面两步:

setState( stateChange ) {
	//其实就是{count:1},{count:2}两个对象就行合并
    Object.assign( this.state, stateChange );//合并接收到的state||stateChange改变的state(setState接收到的参数)
    renderComponent( this );//调用render渲染组件
}

这种实现,每次调用setState都会更新state并马上渲染一次(不符合其更新优化机制,其实就是上面的第一种效果形式,最后返回值是1,并没有达到合并效果),所以我们要合并setState。

setState队列

为了合并setState,我们需要一个队列来保存每次setState的数据,然后在一段时间后执行合并操作和更新state,并清空这个队列,然后渲染组件。
接下来,模拟短时间内发起多个setState操作时,setState的底层原理

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div class="box"></div>
    <script>
        //模拟短时间内发起多个setState操作时,setState的底层原理
        //1.先做一个数组
        //2.短时间内入数组
        //3.最终统一数组,得到最后一个数据再进行渲染
        const arr=[]
        for(var i=0;i<100;i++){
            setState({count:i})
        }

        //将state对象扔进一个数组队列中
        function queueState(state){
            arr.push(state)
        }
        console.log('arr',arr)
        renderComponent(arr)
        //渲染组件的函数
        function renderComponent(arr){
            const box=document.querySelector('.box')
            box.innerHTML=arr[arr.length-1].count
            arr.length=0//渲染完成后清空队列
        }

        //模拟的setState方法
        function setState(state){
            queueState(state)
            console.log('1')
        }
    </script>
</body>
</html>

在这里插入图片描述

setState总结:

  1. 作用: 是用于修改state
  2. 参数: 有两个
    • 第一个参数可以是对象,也可以是回调函数,是用于修改state
      • 第一个参数如果是对象,是不合并的;但是如果是函数,会合并
    • 第二个参数是用于获取最新的state
  3. 注意事项
    • setState不能写在render函数中,因为单线程,任务必须执行完一个之后再执行另一个。否则会报 Maximum update 栈溢出错误
    • 在合成事件中,setState是异步的,但是在原生事件中,是同步的
  4. 核心原理: setState合并
    • 为了性能优化,同一个方法中执行多次的setState,会合并到一个数组中,统一处理
    • 短时间内快速进行了多次的setState操作时,我们的setState会合并成一个
  5. setState原理
    • setState -> 入队列 -> 出队列 -> 清空

上述是个人的理解和总结
深入了解可见博客:https://zhuanlan.zhihu.com/p/44537887

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值