几个月前,我开始停止使用React的 setState 。我并不是不再需要组件状态,而且不再用React来管理我的组件状态。
setState对于新手来说不是很友好,即使是有经验的React程序员在使用setState时,也很容易出bug,比如:
Bug产生的原因是忘记了React的state是异步的;从日志打印延迟可以看出来。
React的已将把使用setState可能会出现的所有问题都总结了:
注意:
永远不要直接修改
this.state
,需要通过调用this.setState
方法来替换你修改后的值。把this.state
当做不可变数据来处理。
setState()
不会马上去改变this.state
,而是会排队等待处理,所以当你调用setState()
后访问this.state
,有可能会返回旧的state
。当你调用
setState()
时,无法保证是同步执行的,因为为了保证性能可能会被批处理。
setState()
总是会触发render()
进行重新渲染,除非在shouldComponentUpdata()
控制了渲染逻辑。如果用了可变数据结构以及在shouldComponentUpdata()
中并没有控制渲染逻辑,调用setState()
将不会触发重新渲染渲染。
总的来说,使用setState
会带来三个问题:
1. setState
是异步的
许多开发人员起初并没有意识到这一点,当你设置了新的state
,却发现没有变化,这是setState
最诡异的地方,因为setState
调用的时候看起来并不是异步。比如下面的代码:
class Select extends React.Component { constructor(props, context) { super(props, context) this.state = { selection: props.values[0] }; } render() { return (
- {this.props.values.map(value =>
- this.onSelect(value)} > {value} )}
乍一看没什么问题,但是这个select
组件有一个bug,上面的git图已经很好的证明了。onSelect()
方法触发时总是得到前一个state.selection
的值,因为setState
还没有完成,fireOnSelect
就被调用了。我认为应该把setState
重新命名为scheduleState
或者要求传入回调。
这个bug很容易修复,棘手的地方在于你很难发现它。
2. setState
引起没有必要的渲染
setState
的第二个问题在于它总是会触发重新渲染,很多时候这种渲染是没有必要的。你可以通过(React提供的性能工具)来检测它会在什么时候重新渲染。从三个方面来粗略的讲为什么重新渲染有时候是没有必要的:
当新的
state
和旧的state
是一样的。可以通过shouldComponentUpdate
来解决,也可以用纯渲染库来解决。只有某些时候
state
的改变才和渲染有关系。第三,某些时候
state
和视图层一点关系都没有,比如用来管理事件的监听器,定时器等相关的state
。
3. setState
不可能管理所有组件的状态
接着上面最后那点说,不是所有的组件状态都需要通过setState
来储存和更新。大多数复杂的组件通常需要管理定时器循环,接口请求,事件等等。如果用setState
来管理,不仅仅会引起没有必要渲染,而且会造成死循环。
用MobX来管理组件状态
代码如下:
import {observable} from "mobx"import {observer} from "mobx-react"@observer class Select extends React.Component { @observable selection = null; /* MobX managed instance state */ constructor(props, context) { super(props, context) this.selection = props.values[0] } render() { return (
- {this.props.values.map(value =>
- this.onSelect(value)} > {value} )}
效果如下:
用同步的组件状态的机制没有出现意想不到的bug。
上面的代码片段不仅简洁美观,MobX
还解决了setState
的全部问题:
改变state
马上就反映到了组件state
上。这让代码逻辑和代码重用变得更简单。你不用去担心state
是否更新了。
MobX
在运行时候确定哪些state
与视图层关联,暂时与视图层无关的State
不会引起重新渲染,直到再次关联。
所以可渲染和不可渲染状态是统一处理的。
此外,直接修改state
对象的低级错误不能再犯了。还有,不要担心是否还能用shouldComponentUpdate 和或者PureRenderMixin方法,MobX
也很好的处理了。最后,你也许会想,我如何等到setState
处理完成呢,你可以使用compentDidUpdate
生命周期。
最后:
我已经停止使用React来管理组件状态了,而是用MobX
代替。现在React
成了“正真”的视图层:)。
以下是实现的Dome:
原文: