React 中的 state(状态)是简单的 JavaScript 对象,用于管理组件内的状态。请牢记:只有用在渲染(render)过程中的数据才保存在状态里。

什么是状态(State) Link to heading

React 组件用状态来保存相关数据,它本身就是一个简单的对象。状态和普通对象的区别在于,React 会监视状态的属性,并在它更新时触发组件重新渲染。

状态更新会触发重新渲染,因此可以只用它来保存渲染所需的数据。换句话说,虽与组件相关但不用于 render 方法的变量就无需保存在状态里了,使用实例变量即可。

同时,你还可以认为状态就是组件的私有数据,只有在组件内才能读取或更新状态。我们不能在它的父组件或子组件里访问状态。

与属性(Props)的区别 Link to heading

props 类似,状态就是一个用于触发组件重新渲染的对象。区别在于 props 来自父组件,状态是组件的内部数据。

我们无法在组件内更新 props。因为它是来自外部的数据,组件不能对其进行操作。状态处于组件内部,组件对它有完全操作的能力。

如下图:

state vs props

初始化状态 Link to heading

我们有多种方式来初始化组件的状态:

  • 使用 getInitialState 方法
  • constructor 方法内初始化
  • 以类属性的方式初始化

使用 getInitialState 方法 Link to heading

使用 React.createClass 来定义组件时,可以使用 getInitialState 方法:

import React from 'react';

const ExampleComponent = React.createClass({
  getInitialState () {
    return {
      someKey: 'someValue'
    };
  },

  render() {
    return (
      <div>{this.state.someKey}</div>
    );
  }
});

export default ExampleComponent;

constructor 方法内初始化 Link to heading

当你使用 ES6 的 Class 关键字定义组件时,可以在 constructor 方法里初始化状态:

import React from 'react';

class ExampleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      someKey: 'someValue'
    };
  }
  render() {
    return (
      <div>{this.state.someKey}</div>
    );
  }
}

export default ExampleComponent;

请不要忘记调用 super() 方法,否则在 constructor 内将无法使用 this。不要漏掉了 super 方法里的 props 参数。

以类属性的方式初始化 Link to heading

同样在使用 ES6 的 Class 关键字时,我们还可以用类属性的方式初始化状态。这种写法可以节省几行代码:

import React from 'react';

class ExampleComponent extends React.Component {
  state = { someKey: 'someValue' }

  render() {
    return (
      <div>{this.state.someKey}</div>
    );
  }
}

export default ExampleComponent;

更新状态 Link to heading

更新状态时,需要留意如下规则:

  • 不要直接修改状态的属性值
  • 警告:setState 是一个异步方法
  • 状态更新会合并操作

不要直接修改状态的属性值 Link to heading

第一条规则就是「不要向任何人提起状态更新」,恩不对,好像进错片场了。第一条规则是:不要直接修改状态的属性值。

// 不要这样做
this.state.someValueInState = 'NEW VALUE';

我们只能在初始化状态时直接写入状态,比如在 constructor 里进行定义。

除了初始化之外,我们只能使用 this.setState 方法来更新状态:

this.setState({someValueInState: 'NEW VALUE'});

警告:setState 是一个异步方法 Link to heading

请留意 setState 的两个关键点:第一,在计算下一个状态时不能依赖 this.statethis.props,它们可能会被异步更新:

// 不要这样做
this.setState({
  counter: this.state.counter + this.props.increment,
});

在这种情况下,用 function 作为 setState 的第一个参数即可:

this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

第二,由于 setState 是异步执行的,因此我们不能依赖 this.setState 之后马上取得的 this.state 值。

如果你要在状态真正完成更新时执行代码,请使用 this.setState 的第二个参数,回调函数:

this.setState({
    someKey: 'someValue',
  }, () => {
    // 只有在状态完成更新后,才会执行这里的代码
  }
);

state 更新会合并操作 Link to heading

当我们调用 setState 方法时,React 将把参数传递的对象合并到当前的状态中。我们不必担心会出现属性覆盖的情况。

假设我们在 constructor 中定义了一个状态:

constructor(props) {
  super(props);
  this.state = {
    someKey: 'someValue',
    someOtherKey: 'someOtherValue'
  };
}

之后,我们可以用 setState 分别更新这两个属性:

this.setState({
  someKey: 'someNewValue'
});

在上面的代码里,this.state.someOtherKey 就不会被覆盖。