理解react之setState

2018-07-12 admin

组件状态(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 <button onClick={this.inc}>Click to update</button>
}

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 <div>The value is: {this.state.value}</div>
  }
}

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

<Component value={42} />

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

<Component value={13} />

那它仍会认为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 <div>The value is: {this.state.value}</div>
  }
}

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

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

原文链接:https://segmentfault.com/a/1190000015606509

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-35787.html

文章标题:理解react之setState

相关文章
何为闭包?有关JS闭包的一些理解
简单一点的说:闭包就是能够读取其他函数内部变量的函数。那如何实现读取其它函数内部变量呢,大家都知道在JavaScript中内部函数可以访问其父函数中的变量,那如果将内部函数返回是不是代表能够通过它访问其父函数中的变量了呢,闭包的原理事实上就...
2015-11-11
JavaScript正则进阶之路——活学妙用奇淫正则表达式
有些童鞋肯定有所疑惑,花了大量时间学习正则表达式,却发现没有用武之地,正则不就是验证个邮箱嘛,其他地方基本用不上,其实,大部分人都是这种感觉,所以有些人干脆不学,觉得又难又没多大用处。殊不知,想要成为编程大牛,正则表达式必须玩转,GitH...
2017-05-31
DOM之通俗易懂讲解
DOM 是所有前端开发每天打交道的东西,但是随着 jQuery 等库的出现,大大简化了 DOM 操作,导致大家慢慢的 “遗忘” 了它的本来面貌。不过,要想深入学习前端知识,对 DOM 的了解是不可或缺的,所以本文力图系统的讲解下 DOM 的...
2016-01-13
JS教程之基础
javascript教程之什么是 JavaScript? JavaScript 被设计用来向 HTML 页面添加交互行为。JavaScript 是一种脚本语言(脚本语言是一种轻量级的编程语言)。JavaScript 由数行可执行计算机代码组...
2015-11-12
ajax教程之ajax使用Http请求
ajax中是如何让使用http请求的呢? 在传统的JS编程中,如果您希望从服务器上的文件或数据库中得到任何的信息,或者向服务器发送信息的话,就必须利用一个 HTML 表单向服务器 GET 或 POST 数据。而用户则需要单击“提交”按钮来发...
2015-11-12
Ajax教程之Ajax介绍
Ajax 由 HTML、JavaScript™ 技术、DHTML 和 DOM 组成,这一杰出的方法可以将笨拙的 Web 界面转化成交互性的 Ajax 应用程序。本文的作者是一位 Ajax 专家,他演示了这些技术如何协同工作 —— 从总体概述...
2015-11-12
数据格式之战:JSON vs XML
在比较JSON和XML之前,我们先来上一堂关于数据格式的简要历史(更准确的说,是关于XML的始祖): 早在1970年,IBM开发了一种叫Generalized Markup Language的标记语言,简称GML,它主要是为脚本语言定义的一...
2016-01-13
bootstrap table之通用方法( 时间控件,导出,动态下拉框, 表单验证 ,选中与获取信息)代码分享
1.bootstrap-table 单击单行选中 $(&#x27;#gzrwTable&#x27;).on(&#x27;click-row.bs.table&#x27;, function(e, row, $element) { $(&#x...
2017-02-17
javascript数据结构与算法之检索算法
查找数据有2种方式,顺序查找和二分查找。顺序查找适用于元素随机排列的列表。二分查找适用于元素已排序的列表。二分查找效率更高,但是必须是已经排好序的列表元素集合。 一:顺序查找 顺序查找是从列表的第一个元素开始对列表元素逐个进行判断,直到找到...
2017-03-22
JavaScript深入之类数组对象与
类数组对象 所谓的类数组对象: 拥有一个 length 属性和若干索引属性的对象 举个例子: var array = [&#x27;name&#x27;, &#x27;age&#x27;, &#x27;sex&#x27;]; var ...
2017-05-27
回到顶部