stackoverflow 有人提问:Does React keep the order for state updates?
我知道 react 的状态更新是异步执行的,为了性能优化,状态是批量更新的。所以你永远不能确信在调用 setState 后状态是否更新了。但是你是否可以确认 setState 调用后状态的更新顺序呢?
比如以下情况:
考虑以下按钮点击的例子:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.setState({ a: true });
this.setState({ b: true });
}
}
class SuperContainer extends React.Component {
constructor(props) {
super(props);
this.state = { a: false };
}
render() {
return <Container setParentState={this.setState.bind(this)}/>
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.props.setParentState({ a: true });
this.setState({ b: true });
}
}
请记住,这是我对用例的极端简化。我知道我可以改善我的代码,例如,在例 1 中同时更新两个 state 值,以及在例 2 中的第一个 setState 的回调中执行第二个 setState。但是,这不是我的问题,而且我只想知道 React 执行这些状态更新时有没有确切的方式和顺序,仅此而已。
不久后,React 的作者 Dan Abramov 亲自来回答了。
I work on React.
开场定乾坤。很有力度的一句话。
TLDR:
很多英文文章中都会出现 TLDR,有时候会写成 tl_dr、tl;dr,是 Too Long; Didn’t Read 的缩写,作用就是告诉读者,这篇内容篇幅比较长,如果不想深入探讨或时间有限,可以看总结。
Dan Abramov 先给出了结论:
状态始终是按照特定的顺序更新的。无论你是否看到介于两个状态之间的一个中间状态,无论你是否在批处理内。
目前(React 16 及更早版本),默认情况下,只有 React 事件处理程序中的更新才会被批处理。有一个不稳定(unstable)的 api 来强制在事件处理程序之外进行批处理,以便在需要时处理罕见的情况。
在未来的版本(可能是 React 17 或更高版本)中,React 将默认批量更新所有更新,因此你不必考虑这一点。与往常一样,我们将在 React 博客和 release note 中宣布对此的任何更改。
理解这一点的关键在于,在 React 事件处理程序中,不论 setState() 调用了多少次,也不论 setState() 被多少个组件调用,它们在事件结束时只会生成一次重新渲染。这对于大型应用程序性能至关重要,因为如果 Child 与 Parent 处理 click 事件的时候都调用 setState(),你不希望重新渲染 Child 两次。
在这两个例子中,setState() 调用都发生在 React 事件处理程序中。因此,在事件结束时,他们总是被合并到一起(而且你看不到中间状态)。
更新总是按照它们发生的顺序进行浅合并(shallowly merge)。所以如果第一次更新是 {a: 10},第二次是 {b: 20},第三次 {a: 30},呈现的状态将是 {a: 30, b: **20}。对 state 中同一个 key 的更新(例如我的例子中 a 的更新),最新(近)的更新总是“胜出”。**
当我们在批处理结束时重新呈现 UI 时,this.state 对象已经被更新了。所以如果你需要根据之前的状态更新状态(比如增加一个计数器),你应该使用 setState(fn) 的回掉函数版本来提供之前的状态,而不是读取状态 this.state。如果你对这个理由感到好奇,我在这个评论中深入地解释了它。
在你的例子中,我们不会看到“中间状态”,因为我们在具有批处理功能的 React 事件处理程序中(因为 React “知道”什么时候退出该事件)。
但是,在 React 16 和更早版本中,React 事件处理程序之外还没有默认的批处理。因此,如果在你的例子中,我们吧 handleClick 替换为 AJAX 处理程序,那么每个 setState() 都会立即处理。在这种情况下,是的,你会看到一个中间状态:
promise.then(() => {
// 我们不在事件处理程序中,因此它们都会被刷新。
this.setState({a: true}); // 使用 {a: true, b: false } 重新渲染
this.setState({b: true}); // 使用 {a: true, b: true } 重新渲染
this.props.setParentState(); // 重新渲染父组件
});
我们认识到,根据是否处于事件处理程序中,行为是不同的,这是不方便的。这将在未来的 React 版本中进行更改,默认情况下将批量更新所有更新(并提供选择性 API 以同步刷新更改)。直到我们切换默认行为(可能在 React 17 中),有一个 API 可以用来强制批量处理:
promise.then(() => {
// 强制批量处理
Reactdom.unstable_batchedUpdates(() => {
this.setState({a: true}); // 不重新渲染
this.setState({b: true}); // 不重新渲染
this.props.setParentState(); // 不重新渲染
});
// 当我们退出 unstable_batchedUpdates函数后,重新渲染一次
});
在 React 内部,事件处理程序都被包装在 unstable_batchedUpdates 内,这就是默认情况下批处理的原因。请注意,将 setState 封装 unstable_batchedUpdates 两次是不起作用的。当我们退出最外层的unstable_batchedUpdates 调用时,更新被刷新。
该 API 是“不稳定的”,因为如果默认情况下已经启用批处理,我们将删除它。但是,在 React 小版本中我们不会删除它,所以在 React 17 之前,如果你需要在 React 事件处理程序之外的某些情况下强制批处理,你可以安全地依赖它。
总之,这是一个令人困惑的话题,因为 React 默认只在事件处理程序中进行批处理。这将在未来的版本中发生变化,那么行为将更直接。但解决方案不是减少批处理,而是默认开启更多的批处理。这就是我们要做的。
如今的 Web 前端已被 React、Vue 和 Angular 三分天下,尽管现在的 jQuery 已不再那么流行,但 jQuery 的设计思想还是非常值得致敬和学习的,特别是 jQuery 的插件化。
受控组件与非受控组件在官网与国内网上的资料都不多,有些人觉得它可有可不有,也不在意。这恰恰显示React的威力,满足不同规模大小的工程需求。
一般在传统模式下,我们构建前端项目很简单。就是下载各种js文件,如JQuery、Echart等,直接放置在html静态文件。Webpack则是JavaScript中比较知名的打包工具。这两个构建工具构成了React应用快速搭建的基础。
Gatsby能快速的使用 React 生态系统来生成静态网站,可以结合React Component、Markdown 和服务端渲染来完成静态网站生成让他更强大。
React推出后,出于不同的原因先后出现三种定义react组件的方式,殊途同归;具体的三种方式:函数式定义的无状态组件、es5原生方式React.createClass定义的组件、es6形式的extends React.Component定义的组件
React主要思想是通过构建可复用组件来构建用户界面,每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。所谓组件就是有限状态机,,表示有限个状态以及在这些状态之间的转移和动作行为的模型。
React 相关的优化:使用 babel-react-optimize 对 React 代码进行优化,检查没有使用的库,去除 import 引用,按需打包所用的类库,比如 lodash 、echarts 等.Webpack 构建打包存在的问题两个方面:构建速度慢,打包后的文件体积过大
这篇文章主要介绍React Router定义路由之后如何传值,有关React和React Router 。react router中页面传值的三种方法:props.params、query、state
react 高阶组件简单的理解是:一个包装了另一个基础组件的组件。高阶组件的两种形式:属性代理(Props Proxy)、反向继承 (Inheritance Inversion)
React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!