博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用React.setState需要注意的三点
阅读量:6316 次
发布时间:2019-06-22

本文共 4191 字,大约阅读时间需要 13 分钟。

原文:

作者: Michel Weststrate


前言

这篇文章原标题是3 Reasons why I stopped using React.setState,但是我对原文作者提出的论点不是很感冒,但是作者提出的三点对React新手来说是很容易忽略的地方,所以我在这里只提出部分内容,而且把标题改为使用React.setState需要注意的三点

正文

React新手来说,使用setState是一件很复杂的事情。即使是熟练的React开发,也很有可能因为React的一些机制而产生一些bug,比如下面这个例子:

由于异步的setState造成的bug。log永远比当前的慢一步

中也说明了当使用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 (      
    {this.props.values.map(value =>
  • this.onSelect(value)} > {value}
  • )}
) } 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(

第一眼看上去,这个代码似乎没有什么问题。两个事件处理中调用onSelect方法。但是,这个Select组件中有一个bug很好的展现了之前的GIF图。onSelect方法永远传递的是之前的state.selection值,因为当fireOnSelect调用的时候,setState还没有完成它的工作。我认为React至少要把setState改名为scheduleState或者把回掉函数设为必须参数。

这个bug很容易修改,最难的地方在于你要知道有这个问题。

2. setState会造成不必要的渲染

setState造成的第二个问题是:每次调用都会造成重新渲染。很多时候,这些重新渲染是不必要的。你可以用React performance tools中的来查看什么时候会发生不必要渲染。但是,大概的说,不必要的渲染有以下几个原因:

  • 新的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改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。或者参考原文中的MobX

避免不必要的修改,当state的值没有发生改变的时候,尽量不要使用setState。虽然shouldComponentUpdatePureComponent可以避免不必要的重复渲染,但是还是增加了一层shallowEqual的调用,造成多余的浪费。

以上

转载地址:http://abuaa.baihongyu.com/

你可能感兴趣的文章
MySQL表名不区分大小写的设置方法
查看>>
C primer plus 第5章 运算符、表达式和语句 5.1循环简价
查看>>
JDK8 Collectors 使用篇(四)
查看>>
分分钟掌握阻塞/非阻塞/同步/异步IO模型
查看>>
职场新人5招远离办公室“政治”
查看>>
Apache commons类库阅读笔记
查看>>
spring 配置文件中使用占位符${}
查看>>
spring自定义事件和事件监听器以及事件的发布-ApplicationEvent
查看>>
构建简单的C++运行时反射类型系统
查看>>
python3常见报错及解决方法(ModuleNotFoundError、SyntaxError、TypeError、NameError)
查看>>
SQL JOIN
查看>>
使用PHPMAILER实现PHP发邮件功能
查看>>
python 国内镜像
查看>>
如何在文本对比工具合并文本时设置对齐方式
查看>>
STM32各种时钟的区别
查看>>
合成图 + 跨域名静态文件服务器(cookie-free domains)
查看>>
linux驱动安装
查看>>
JDK动态代理详解
查看>>
Linux中的tty与pt
查看>>
Jenkins Failed to archive test report
查看>>