react学习—深入剖析setState

一、深入剖析setState

1、setState是同步还是异步

情景

//index.ja
import React from 'react';
import ReactDOM from 'react-dom';
import Comp from './Comp'

ReactDOM.render(<Comp n={0} />, document.getElementById('root'));
//Comp.js
import React, { Component } from 'react'

export default class Comp extends Component {
  state={
    n: this.props.n
  }
  handleClick=()=>{
    this.setState({
      n: this.state.n + 1
    })
    console.log(this.state.n);
  }
  render() {
    return (
      <div>
        <h1>{this.state.n}</h1>
        <button onClick={this.handleClick}>点击+</button>
      </div>
    )
  }
}

不难理解该代码的意思是想每次点击使n+1.并且输出n的值。但是实际情况并非如此。
在这里插入图片描述
在这里插入图片描述
我们发现页面渲染的值变了,但是输出的n值仍是0。那么改进下代码。

export default class Comp extends Component {
  state={
    n: this.props.n
  }
  handleClick=()=>{
    this.setState({
      n: this.state.n + 1
    })
    console.log(this.state.n);
  }
  render() {
    console.log(render);
    return (
      <div>
        <h1>{this.state.n}</h1>
        <button onClick={this.handleClick}>点击+</button>
      </div>
    )
  }
}

render会先渲染一次这里不难理解。
在这里插入图片描述
点击改变n的状态值,输出顺序也和我们预想的一样,状态先改变,然后触发render渲染。
在这里插入图片描述
那么这里我们就得出了答案:setState,它对状态的改变,可能是异步的
为什么说是可能呢?
回忆一下前面的学习笔记不难发现,我们在构造一个倒计时的组件时

import React, { Component } from 'react'

export default class Tick extends Component {
  state = {
    time: this.props.time
  }
  constructor(props){
    super(props);
    let timer = setInterval(()=>{
      this.setState({
        time: this.state.time - 1
      });
      if(this.state.time === 0){
        clearInterval(timer);
      }
    },800);
  }
  render() {
    return (
      <div>
        <h1>{this.state.time}</h1>
      </div>
    )
  }
}

这里很明显就是同步的改变状态,因此在time === 0时我们才能够清空定时器。
在这里插入图片描述
这里我们观察两种代码的一同,可以得出一个结论:
如果改变状态的代码处于某个HTML元素的事件中,则其是异步的(改变n的click事件是绑定在button上),否则是同步(倒计时组件未进行任何绑定)

2、如何及时获取异步后的状态值

如果我们有某些特殊的需求,需要连续改变某状态的值

export default class Comp extends Component {
  state={
    n: this.props.n
  }
  handleClick=()=>{
    this.setState({
      n: this.state.n + 1
    })
    this.setState({
      n: this.state.n + 1
    })
    this.setState({
      n: this.state.n + 1
    })
  }
  render() {
    console.log('render');
    return (
      <div>
        <h1>{this.state.n}</h1>
        <button onClick={this.handleClick}>点击+</button>
      </div>
    )
  }
}

我们会发现这样是无效,远近就是这里异步改变,我们每次执行时n的值还是0
在这里插入图片描述
为此react特意为我们提供了setState的回调函数以及时获取状态改变后的值。

  handleClick=()=>{
    this.setState({
      n: this.state.n + 1
    },()=>{
      console.log(this.state.n);
      this.setState({
        n: this.state.n + 1
      },()=>{
        console.log(this.state.n);
      })
    });
  }

在这里插入图片描述
此时又有了新的问题,如果碰到这种多次改变状态值得需求,将产生难以阅读的回调代码。于是setState有提供了函数类参数。

  handleClick=()=>{
//参数cur表示当前的状态
//该函数的返回结果,会混合(覆盖)掉之前的状态
//该函数也是异步执行
    this.setState(cur => {
      return {
        n: cur.n + 1
      }
    },()=>{
      console.log(this.state.n);
    });
  }

在这里插入图片描述
这个函数参数的写法,会使cur参数的值变得值得信赖。(它会形成一个队列,等待前一个状态改变后将新的状态值给新的setState调用);

  handleClick=()=>{
    this.setState(cur => ({
        n: cur.n + 1
      }
    ));
    this.setState(cur => ({
        n: cur.n + 1
      }
    ));
    this.setState(cur => ({
        n: cur.n + 1
      }
    ));
  }

在这里插入图片描述
如果遇到某个事件中,需要同步调用多次,需要使用函数的方式得到最新状态。

setState最佳实践:

  1. 把所有的setState当作是异步的
  2. 永远不要信任setState调用之后的状态
  3. 如果要使用改变之后的状态,需要使用回调函数(setState的第二个参数)
  4. 如果新的状态要根据之前的状态进行运算,使用函数的方式改变状态(setState的第一个函数)

3、react的render优化

React会对异步的setState进行优化,将多次setState进行合并(将多次状态改变完成后,再统一对state进行改变,然后触发render)

  handleClick=()=>{
    this.setState(cur => ({
        n: cur.n + 1
      }
    ),()=>{
      console.log(this.state.n);
    });
    this.setState(cur => ({
        n: cur.n + 1
      }
    ));
    this.setState(cur => ({
        n: cur.n + 1
      }
    ));
  }
  render() {
    console.log('render');
    return (
      <div>
        <h1>{this.state.n}</h1>
        <button onClick={this.handleClick}>点击+</button>
      </div>
    )
  }

这里虽然state.n状态改变了三次,但是由于是异步的setState,所以只触发最终的render,也包括了setState的回调函数此时为3。
在这里插入图片描述

博主开始运营自己的公众号啦,感兴趣的可以关注“飞羽逐星”微信公众号哦,拿起手机就能阅读感兴趣的博客啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞羽逐星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值