setState总结

时间:2021-1-8 作者:admin

State

React 组件中的数据可以来源于使用者,也可以组件自身维护。使用者传递的数据就是组件的属性 (props),而组件自身维护的数据就是组件的状态(state)。

React 中的哲学:数据属于谁,谁才有权力更改。

对于使用者传递过来的 props,组件自然是没有权利更改的;对于 state,组件自己自行维护,所以组件自然是有权利更改的。

在表现上,stateprops 一样都是一个对象,但是 state 仅在类组件中有效。

setState

由于 React 无法监测到组件状态的变化,这也意味着我们无法直接修改 state 的某个属性值。

就像这样:

class Demo extends React.Component {    state = {        a: 1    }    render() {        return (            <div>                <p> {this.state.a}</p>                <button onClick={() => { this.state.a-- }}>+1</button>            </div>        )    }}

可以看到,React 不允许我们直接在状态上修改,而且这样做无法实现我们想要的效果(页面上的值没有发生变化)。

要想修改 state 的值,必须使用 this.setState({}) 改变状态,传入的参数为一个对象,需要修改的值就是对象的键和值,React 会将该对象和原本的 state 进行混合(Mixin),然后会导致当前的组件重新渲染,也就是调用 render 函数(手动调用无效)。

对于前面的例子我们需要将事件处理函数进行修改:

<button onClick={() => { this.setState({a: this.state.a + 1}) }}>+1</button>

深入 setState

我们知道 Vue 的渲染是异步执行的,那 React 呢?

我们先更改一下 render 函数:

render() {    console.log('render');    return (        <div>            <p> {this.state.a}</p>            <button onClick={() => {                    this.setState({ a: this.state.a + 1 });                    console.log(this.state.a);                }}>+1</button>        </div>    )}

前面说过,调用 setState 会导致调用 render,触发页面的重新渲染,但是调用了 setState 之后,a 的值并未立刻改变,而是事件处理函数运行之后才触发,由此可以看出这里的 setState 函数是异步的。

真的是这样吗?

我们来添加一个声明周期函数:

componentDidMount() {    this.timer = setInterval(() => {        this.setState({ a: this.state.a + 1 });        console.log(this.state.a);        if (this.state.a === 3) {            clearInterval(this.timer);        }    }, 1000);}

这好像和我们的预期不符,怎么 setState 又变成同步的了?

这意味着,React 中 setState 可能是同步也可能是异步的,这和 Vue 是不一样的。

那么什么时候是同步什么时候是异步?

结论:如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,否则是同步

上面的例子可以很好的印证,当 setState 出现在 时间处理函数中,就是异步的,其他地方则是同步。

如果我们需要使用 setState 改变状态后的数据怎么办?

setState 还有第二个参数,一个回调函数,会在页面重新渲染之后调用,这时就可以获取到正确的数据。

<button onClick={() => {        this.setState({ a: this.state.a + 1 }, () => {            console.log(this.state.a);        });    }}>+1</button>

如果我们需要对 state 进行多次改变,每次改变需要前一次改变的状态,就可以将 setState 的第一个参数改为 一个函数:

函数的参数是当前的状态(state),函数的返回值就是需要修改的 state

handleClick = () => {    this.setState(state => {        console.log('--1--', this.state.a);        return {            a: state.a + 1        }    }, () => {        console.log(this.state.a);    });    this.setState(state => {        console.log('--2--', this.state.a);        return {            a: state.a + 1        }    });    console.log('aaaa');}<button onClick={this.handleClick}>+1</button>

这样写并不会改变 setState 同步或是异步的场景,该同步的时候还是同步执行,该异步还是异步。只不过在异步的场景下,这些连续的 setState并不会每次都触发 render,这些函数参数会被一起连续的异步执行完毕之后再触发 render,而 setState 的第二个参数也会等待 render 之后才会执行。

最佳实践:

  1. 把所有的 setState 当作是异步的
  2. 永远不要信任 setState 调用之后的状态
  3. 如果要使用改变之后的状态,需要使用回调函数(setState 的第二个参数)
  4. 如果新的状态要依赖之前的状态,使用函数的方式改变状态(setState 的第一个参数)
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。