使用React.setState有哪些需要注意的地方
本篇文章主要介绍了浅谈使用React.setState需要注意的三点,提出了三点对 React 新手来说是很容易忽略的地方,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
前言
这篇文章原标题是 3 Reasons why I stopped using React.setState ,但是我对原文作者提出的论点不是很感冒,但是作者提出的三点对 React 新手来说是很容易忽略的地方,所以我在这里只提出部分内容,而且把标题改为 使用React.setState需要注意的三点 。
正文
对 React 新手来说,使用 setState 是一件很复杂的事情。即使是熟练的 React 开发,也很有可能因为 React 的一些机制而产生一些bug,比如下面这个例子:
文档 中也说明了当使用 setState 的时候,需要注意什么问题:
注意:
绝对不要 直接改变 this.state ,因为之后调用 setState() 可能会替换掉你做的改
变。把 this.state 当做是不可变的。
setState() 不会立刻改变 this.state ,而是创建一个即将处理的 state 转变。在调用该方法之后访问 this.state 可能会返回现有的值。
对 setState 的调用没有任何同步性的保证,并且调用可能会为了性能收益批量执行。
setState() 将总是触发一次重绘,除非在 shouldComponentUpdate() 中实现了条件渲染逻辑。如果可变对象被使用了,但又不能在 shouldComponentUpdate() 中实现这种逻辑,仅在新 state 和之前的 state 存在差异的时候调用 setState() 可以避免不必要的重新渲染。
总结出来,当使用 setState 的时候,有三个问题需要注意:
1. setState是异步的(译者注:不保证同步的)
很多开发刚开始没有注意到 setState 是异步的。如果你修改一些 state ,然后直接查看它,你会看到之前的 state 。这是 setState 中最容易出错的地方。 setState 这个词看起来并不像是异步的,所以如果你不假思索的用它,可能会造成 bugs 。下面这个例子很好的展示了这个问题:
class Select extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
selection: props.values[0]
};
}
render() {
return (
<ul onKeyDown={this.onKeyDown} tabIndex={0}>
{this.props.values.map(value =>
<li
className={value === this.state.selection ? 'selected' : ''}
key={value}
onClick={() => this.onSelect(value)}
>
{value}
</li>
)}
</ul>
)
}
onSelect(value) {
this.setState({
selection: value
})
this.fireOnSelect()
}
onKeyDown = (e) => {
const {values} = this.props
const idx = values.indexOf(this.state.selection)
if (e.keyCode === 38 && idx > 0) { /* up */
this.setState({
selection: values[idx - 1]
})
} else if (e.keyCode === 40 && idx < values.length -1) { /* down */
this.setState({
selection: values[idx + 1]
})
}
this.fireOnSelect()
}
fireOnSelect() {
if (typeof this.props.onSelect === "function")
this.props.onSelect(this.state.selection) /* not what you expected..*/
}
}
ReactDOM.render(
<Select
values={["State.", "Should.", "Be.", "Synchronous."]}
onSelect={value => console.log(value)}
/>,
document.getElementById("app")
)第一眼看上去,这个代码似乎没有什么问题。两个事件处理中调用 onSelect 方法。但是,这个 Select 组件中有一个 bug 很好的展现了之前的 GIF 图。 onSelect 方法永远传递的是之前的 state.selection 值,因为当 fireOnSelect 调用的时候, setState 还没有完成它的工作。我认为 React 至少要把 setState 改名为 scheduleState 或者把回掉函数设为必须参数。
这个bug很容易修改,最难的地方在于你要知道有这个问题。
2. setState会造成不必要的渲染
setState 造成的第二个问题是:每次调用都会造成重新渲染。很多时候,这些重新渲染是不必要的。你可以用 React performance tools 中的 printWasted 来查看什么时候会发生不必要渲染。但是,大概的说,不必要的渲染有以下几个原因:
新的 state 其实和之前的是一样的。这个问题通常可以通过 shouldComponentUpdate 来解决。也可以用 pure render 或者其他的库赖解决这个问题。
通常发生改变的 state 是和渲染有关的,但是也有例外。比如,有些数据是根据某些状态来显示的。
第三,有些 state 和渲染一点关系都没有。有一些 state 可能是和事件、 timer ID 有关的。
3.setState并不能很有效的管理所有的组件状态
基于上面的最后一条,并不是所有的组件状态都应该用 setState 来进行保存和更新的。复杂的组件可能会有各种各样的状态需要管理。用 setState 来管理这些状态不但会造成很多不需要的重新渲染,也会造成相关的生命周期钩子一直被调用,从而造成很多奇怪的问题。
后话
在原文中作者推荐了一个叫做 MobX 的库来管理部分状态,我不是很感冒,所以我就不介绍。如果感兴趣的,可以通过最上面的链接看看原文中的介绍。
基于上面提出的三点,我认为新手应该注意的地方是:
setState 是不保证同步的
setState 是不保证同步的,是不保证同步的,是不保证同步的。重要的事情说三遍。之所以不说它是异步的,是因为 setState 在某些情况下也是同步更新的。 可以参考这篇文章
如果需要在 setState 后直接获取修改后的值,那么有几个方案:
传入对应的参数,不通过 this.state 获取
针对于之前的例子,完全可以在调用 fireOnSelect 的时候,传入需要的值。而不是在方法中在通过 this.state 来获取
使用回调函数
setState 方法接收一个 function 作为回调函数。这个回掉函数会在 setState 完成以后直接调用,这样就可以获取最新的 state 。对于之前的例子,就可以这样:
this.setState({
selection: value
}, this.fireOnSelect)使用setTimeout
在 setState 使用 setTimeout 来让 setState 先完成以后再执行里面内容。这样子:
this.setState({
selection: value
});
setTimeout(this.fireOnSelect, 0);直接输出,回调函数, setTimeout 对比
componentDidMount(){
this.setState({val: this.state.val + 1}, ()=>{
console.log("In callback " + this.state.val);
});
console.log("Direct call " + this.state.val);
setTimeout(()=>{
console.log("begin of setTimeout" + this.state.val);
this.setState({val: this.state.val + 1}, ()=>{
console.log("setTimeout setState callback " + this.state.val);
});
setTimeout(()=>{
console.log("setTimeout of settimeout " + this.state.val);
}, 0);
console.log("end of setTimeout " + this.state.val);
}, 0);
}如果val默认为0, 输入的结果是:
Direct call 0
In callback 1
begin of setTimeout 1
setTimeout setState callback 2
end of setTimeout 2
setTimeout of settimeout 2
和渲染无关的状态尽量不要放在 state 中来管理
通常 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。
避免不必要的修改,当 state 的值没有发生改变的时候,尽量不要使用 setState 。虽然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重复渲染,但是还是增加了一层 shallowEqual 的调用,造成多余的浪费。
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
在vue中使用axios实现文件上传
使用gulp如何创建完整的项目流程
在js中如何实现将数组添加到对象中
在jQuery中如何实现动态控制页面元素
在canvas中如何实现轨迹回放功能
软件开发平台有哪些?
-React、Angular、Vue.js:用于构建前端Web应用。4.移动应用开发平台:-Flutter:Google推出的UI工具包,用于跨平台移动应用开发。-ReactNative:Facebook开发的框架,用于使用JavaScript构建原生移动应用。-Xamarin:允许使用C#构建跨平台移动应用。5.数据科学与人工智能平台:-TensorFlow:用于机器学习和深度学习...
Nike+React+Frenzy+SE篮球鞋可以水洗吗?
篮球鞋可以水洗。关于篮球鞋的清洗,针对不同的材料应该有不同的方法。鞋的外底以及侧面非尼龙材料的部分,可以喷少许衣领净,过十几秒后拿软毛牙刷轻轻刷洗,但是刷洗的时间不宜过长,在刷完之后应该及时用温水或者凉水把泡沫冲走,尽量减少化学物品对鞋的侵蚀的时间。清洗过后应马上用干抹布将残余的水...
软件开发要使用多久
正如 Bit.dev 所提到的,这一组件平台也适合用作设计系统构建器。它能让你的开发人员和设计师团队共同工作,是从头开始构建设计系统的理想工具。Bit.dev 现在支持 React、Vue、Angular、Node 等 JavaScript 框架。6. CanIUse CanIUse 是一款在线工具,使用起来非常方便,因为它让你可以了解所实现的特性...
跑鞋排行榜前十名
5. Nike React Infinity Run Flyknit 这款跑鞋以其React泡沫中底而著称,为跑者提供了持久的舒适性和出色的能量回归。Infinity Run还具有耐用的Flyknit鞋面和专为长距离跑步设计的支撑性。6. Nike Jordan React Havoc SE Jordan React Havoc SE结合了React泡沫科技和经典的Jordan设计,为篮球运动提供了出色...
包琴依洁: setState(object nextState[,function callback]) 合并nextState 和当前 state.这是在事件处理函数中和请求回调函数中触发 UI 更新的主要方法.另外,也支持可选的回调函数,该函数在 setState 执行完毕并且组件重新渲染完成之后调用.
溧水县13479394585: 问react中 componentWillMount,componentDidMount有什么用处 - ?
包琴依洁:react中 componentWillMount,componentDidMount用处是: componentWillMount 组件出现前就是dom还没有渲染到html文档里面,componentDidMount 组件渲染完成,已经出现在dom文档里,可以在各个周期实现特定的操作.一、...
溧水县13479394585: 如何测试react中的this.state - ?
包琴依洁: state表示react组件内部的一种状态,通过组件内的getInitialState函数,可以为组件的初始状态赋值,当组件的状态发生改变时,组件会重新渲染.官方对state的说明如下:组件其实是状态机(State Machines) React 把用户界面当作...
溧水县13479394585: react 哪些生命周期setstate会触发重复渲染 - ?
包琴依洁: 如果直接操作的state对象需要调用forceUpdate方法 如果用setState 就会自动重新渲染的
溧水县13479394585: 为什么在react componentWillUpdate 中调用setState会造成循环调用 - ?
包琴依洁: 如果在shouldComponentUpdate和componentWillUpdate中调用了setState,此时this._pendingStateQueue != null,则performUpdateIfNecessary方法就会调用updateComponent方法进行组件更新.但是updateComponent方法又会调用shouldComponentUpdate和componentWillUpdate,因此造成循环调用,使得浏览器内存占满后崩溃.
溧水县13479394585: react.js 怎么更改input的value值 - ?
包琴依洁: 在react中是无法直接更改from表单元素的值的,必须通过setState()去响应用户的输入.例如想要更改input的value,则需要监听onChange()事件,然后通过event.target.value来获取用户的输入,再通过设置一个名为value的state,来告诉react重新渲染.onChange(event){this.setState({value:event.target.value});}
溧水县13479394585: React 中,如果state中有一个较深层的值改变了,怎么去setState - ?
包琴依洁: 可以直接使用react的immutability helpers var update = require('react-addons-update'); var newData = update(this.state, {c: {d: {1: {e: {$set: 3}}}}}); this.setState(newData)
溧水县13479394585: react this.setstate和this.state的区别 - ?
包琴依洁: this.state通常是用来初始化state的,this.setstate是用来修改state值的.如果你初始化了state之后再使用this.state,之前的state会被覆盖掉,如果使用this.setState,只会替换掉相应的state值
溧水县13479394585: React怎么修改state数组中的数据 - ?
包琴依洁: 我用reactjs生成了html之后,怎么去重新更新这些html呢? 使用this.setState()方法重新设置state是可以 但是如果我要在外部,比如点击某个按钮来更新,而这个按钮不是用reactjs生成的