资讯专栏INFORMATION COLUMN

理解react之setState

FingerLiu / 1865人阅读

摘要:组件状态是一种持有,处理和使用信息的方式。更新唯一你能直接写的地方应该是组件的构造函数中。是异步的事实上会引起的一致性处理重新渲染组件树的过程,是下一个属性的基础即是异步的。常见错误其中最常见的错误之一就是在构造函数中使用设置的值。

组件状态(state)是一种持有,处理和使用信息的方式。state包含的信息仅作用于一个给定组件的内部,并允许你根据它实现组件的一些逻辑。state通常是一个POJO(Plain Old Java[Script] Object)对象,改变它是使得组件重新render自己的方式之一。

state是react背后原理的重要基础概念之一,但是它也有一些特点使得它用起来会有点难以捉摸并且有可能会导致在你的应用中出现一些预料之外的行为。

更新State
唯一你能直接写this.state的地方应该是组件的构造函数中。在其它所有地方你都应该使用this.setState函数,它接受一个对象作为参数,这个对象最终会被合并到组件的当前状态中。

而在技术上你是可以通过this.state={//a new object}这种方式直接修改状态的,但是它不会引起组件使用新的值去重新渲染,然后导致状态不一致的问题。

setState是异步的
事实上setState会引起的一致性处理(reconciliation)——重新渲染组件树的过程,是下一个属性的基础即setState是异步的。这允许我们在单个作用域内多次调用setState而不会触发不必要的重新渲染整个树。

这就是为什么在你更新state后并不能立马看见新的值。

// 假设 this.state = { value: 0 }
this.setState({
  value: 1
});
console.log(this.state.value); // 0

React 也会尝试将多次setState调用组合或者批处理为一次调用:

// 假设 this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

在上面所有的调用完成后,this.state.value将是1,而不是我们的期望值3。那么怎么得到期望值3呢?

setState接受一个函数作为它的参数
如果你传递一个函数作为setState的第一个参数,React将会使用在当前调用时刻的state去调用它并期望你返回一个对象合并到state中。所以你可以把我们上面的代码改成下面这样即可:

// 假设 this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

这样this.state.value 的值就是 3了,就和上面我们认为期望值应该是3相一致了。
记住当在更新state为一个值的时候应始终使用这种语法,它的计算是基于前面的一个状态的。

setState是同步的吗???
记住你刚才学习到setState是异步的。事实证明并非始终如此。这取决于执行上下文,请看下面的例子:

render() {
  return 
}
  
inc() {
  console.log("before: " + this.state.test);
  this.setState({
    test: this.state.test+1
  });
  console.log("after: " + this.state.test);
}

点击按钮元素将会导致你的console中显示:

// click!
before: 1
after: 1
// click!
before: 2
after: 2

但如果我们添加以下代码:

componentDidMount() {
  setInterval(this.inc, 1000);
}

我们将在console中看到:

before: 1
after: 2
before: 2
after: 3

因此,我们需要学习什么时候期望得到哪种行为吗?并不是如此。假设setState的确是异步是非常安全的,因为在未来它就是如此。

setState接受一个回调函数
如果你需要执行一些函数,或者验证状态是否真的有更新正确。你还可以给setState传递一个函数作为第二个参数,这个函数会在状态更新完毕后得到执行。请记住由于单个块内的所有更新会被合并成一个,这将导致每个setState中的回调中得到的state值是全更新的state。

另外一种可以保证你的代码执行是在更新完成以后的方式是将执行代码放在componentWillUpdate 或者 componentDidUpdate中。然而对比回调函数的方式,这两个方法会在shouldComponentUpdate中阻止你的组件更新时不会被调用。

常见错误
其中最常见的错误之一就是在构造函数中使用props设置state的值。考虑如下代码:

class Component extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: this.props.value };
  }
  
  render() {
    return 
The value is: {this.state.value}
} }

如果它的父组件这样render它:

它将会正确渲染value为42,但如果父组件中修改成如下:

那它仍会认为this.state.value是42,这是因为React并不会销毁组件并重新创建它——它会重用一旦渲染好的组件,并且不会重新执行构造函数。要避免这个问题,你应该不要将props赋值给state,而应在render方法中使用this.props.value。
如果你还是想要使用state(如果你的props是以一种非常复杂的计算的使用模式,你不希望每一次render都执行这些复杂的计算),你还可以实现一种在需要的时候才去更新state的解决方案,例如:

class Component extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: this.props.value };
  }
  componentWillReceiveProps(nextProps) {
    if(nextProps.value !== this.props.value) {
      this.setState({value: nextProps.value});
    }
  }
  render() {
    return 
The value is: {this.state.value}
} }

请记住任何componentWill*函数都不是一个合适的地方去触发side effect(如ajax请求),所以请使用
componentDidUpdate(previousProps, previousState),同样也提供和上面类似的if防护语句确保在没有变化时不会执行相关代码。

写在最后的话:这篇文章在Medium中获得超过2.1K的赞,挺不错的,值得翻译!
翻译如有不正,欢迎留言指出!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96064.html

相关文章

  • React系列 Flux架构模式

    摘要:原文地址由于只涉及层的处理,所以构建大型应用应该搭配一个框架模式才能使后期维护成本相对较小正是官方给出的应用架构,他推崇一种单向的数据流动模式,看下图感受下整个流程是用户与层交互,触发使用进行分发触发回调进行更新更新触发层事件层收到信号进 原文地址:https://gmiam.com/post/react-... 由于 React 只涉及 UI 层的处理,所以构建大型应用应该搭配一个框...

    whjin 评论0 收藏0
  • 少妇白洁系列React StateUp模式

    摘要:一般这种情况会在类的构造函数内创建一个属性,引用或词法域的,但后面会看到我们有更好的办法,避免这种手工代码。 换句话说,StateUp模式把面向对象的设计方法应用到了状态对象的管理上,在遵循React的组件化机制和基于props实现组件通讯方式的前提之下做到了这一点。 ---- 少妇白洁 阅读本文之前,请确定你读过React的官方文档中关于Lifting State Up的论述: ht...

    jaysun 评论0 收藏0
  • React Hooks 解析(上):基础

    摘要:第一次了解这项特性的时候,真的有一种豁然开朗,发现新大陆的感觉。为了解决这一痛点,才会有剪头函数的绑定特性。它同时具备和三个生命周期函数的执行时机。 欢迎关注我的公众号睿Talk,获取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 React Hooks 是从 v16.8 引入的又一开创性的新特性。第一次了解这项特性...

    yy736044583 评论0 收藏0
  • 「译」setState如何知道它该做什么?

    摘要:本文翻译自原作者如果有任何版权问题,请联系当你在组件中调用时,你觉得会发生什么当然,会用这条状态重新渲染组件并且更新匹配到的,然后返回元素。如果你之前使用过一些渲染器比如说,你可能知道在页面中使用超过一个渲染器是没什么问题的。 本文翻译自:How Does setState Know What to Do?原作者:Dan Abramov 如果有任何版权问题,请联系shuirong199...

    OldPanda 评论0 收藏0
  • React 应用设计道 - curry 化妙用

    摘要:右侧展现对应产品。我们使用命名为的对象表示过滤条件信息,如下此数据需要在组件中进行维护。因为组件的子组件和都将依赖这项数据状态。化应用再回到之前的场景,我们设计化函数,进一步可以简化为对于的偏应用即上面提到的相信大家已经理解了这么做的好处。 showImg(https://segmentfault.com/img/remote/1460000014458612?w=1240&h=663...

    sewerganger 评论0 收藏0

发表评论

0条评论

FingerLiu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<