土豆博客

React之state与setState

# 一、概述

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。如果你学过Vue,你可以把它理解为Vue中的data 对象。在React中,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)即可。

注意:state仅存在于有状态组件中

例如,在下面的例子中,我们在组件中把age这个状态存储在 state 中,然后在render()函数中通过this.state.age获取到组件中的状态

import React, { Component } from 'react';

export class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      age:10
    }
  }
  render() {
    return (
      <div>
        <p>{this.state.age}</p>
      </div>
    );
  }
}

export default Demo;

接下来,我们继续实现一个需求:点击按钮修改state中的age值,需求很简单,直接开写

import React, { Component } from 'react';

export class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      age:10
    }
  }
  handleClick = () => {
    this.state.age = 20
  }
  render() {
    return (
      <div>
        <p>{this.state.age}</p>
        <button onClick={this.handleClick}>修改</button>
      </div>
    );
  }
}

export default Demo;,

可以看到,我们在页面中添加一个button按钮,并为其添加了点击事件,在点击事件中试图通过this.state.xxx = xxx的形式改变state中的状态,但是保存运行发现,无论如何点击修改按钮,并不起重要,页面上的值并没有发生改变,控制台给出警告:

Do not mutate state directly. Use setState() react/no-direct-mutation-state

这也就意味着,state中的状态不能直接用 this.state.xxx = xxx 这种方式来修改,如果这样做 React就没办法知道你修改了组件的状态,它也就没有办法更新页面。所以我们要通过React提供的 this.setState 方法进行更新,setState 方法由父类 Component 所提供。当我们调用这个函数的时候,React会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上,代码如下:

import React, { Component } from 'react';

export class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      age:10
    }
  }
  handleClick = () => {
    this.setState({
      age:this.state.age + 1
    })
  }
  render() {
    return (
      <div>
        <p>{this.state.age}</p>
        <button onClick={this.handleClick}>按钮</button>
      </div>
    );
  }
}

export default Demo;

可以看到,当我们重新保存运行的时候,点击按钮,页面上的数据发送了改变,这样数据就实现了更新效果。

# 二、注意点

这样看上去似乎已经完美了,但是还有几点需要注意的:

(1)当你调用 setState 的时候,React并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state当中,也就意味着setState是异步的,如果上面通过setState修改状态,下面立马要获取最新的数据,是获取不到的,依然拿上面的栗子:

handleClick = () => {
    this.setState({
        age:this.state.age + 1
    })
    console.log(this.state.age) //获取更新后的数据
}

上述代码,修改state中的age之后,想要获取最新的age的值,但是保存运行,点击按钮后控制台打印的值仍为10,而不是11,这并不是什么 bug,只是 React的 setState 把你的传进来的状态缓存起来,稍后才会帮你更新到 state 上,所以你获取到的还是原来的age。如果你想要获取到最新的数据,这就需要用到setState的第二个参数,它可以接受一个函数,而在函数里面你可以获取到最新的值

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

这样的话,点击按钮,可以看到控制台打印的是11,即更新后的值

(2)所以如果你想在 setState 之后使用新的 state 来做后续运算,看了第一个注意点,你可能会这样写

handleClick = () => {
    this.setState({
        age:this.state.age + 1
    },()=>{
        this.setState({
            age:this.state.age + 2
        })
    })
}

可以看到,我们拿到最新的值后,接着进行二次运算,继续在新的值上进行+1操作,这样看起来没什么毛病,点击按钮打印出来的值也是预期的13,但是如果继续多次的后续操作,代码循环嵌套,看起来很怪异,就像这样

handleClick = () => {
    this.setState({
        age:this.state.age + 1
    },()=>{
        this.setState({
            age:this.state.age + 2
        },()=>{
            this.setState({
                age:this.state.age + 3
            })
        })
    })
}

这时候,我们可以用另一种方式,setState的第一个参数,也可以改写为函数形式,即可以接受一个函数作为参数,React会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象,这样就可以达到上述的利用上一次 setState 结果进行运算的效果。

handleClick = () => {
    this.setState((state)=>{
        return {
            age:state.age + 1
        }
    })
    this.setState((state)=>{
        return {
            age:state.age + 2
        }
    })
    this.setState((state)=>{
        return {
            age:state.age + 3
        }
    })
}
code
top

扫码添加,一起进步

wechat-code

为了保障最佳预览体验,博客已不支持IE浏览器的访问,邀请您使用以下现代高级浏览器。

谷歌浏览器(推荐) 火狐浏览器

注:如果你使用的是360,QQ等双核浏览器,请开启极速模式