一文解锁 React 中绑定事件处理函数的新姿势!

关键时刻,第一时间送达!

React中的事件处理函数绑定看似容易,但还是需要一些技巧。如果你了解Perl和Python的历史,就应该知道TMTOWTDI和TOOWTDI这两个词的意思:Perl的理念是TMTOWTDI,即There's More Than One Way To Do It(完成一项任务有很多种方法),而Python的理念是TOOWTDI,即There's Only One Way To Do It(完成一项任务只有一种方法)。在历史上,Perl曾以这种多样性闻名,但实践证明,过多的实现方法只会给代码造成混乱,从软件工程的角度来看,它远远不如Python奉行的单一方法理念。很不幸,JavaScript就是个TMTOWTDI语言,所以混乱也是在所难免了。

这篇文章将探讨在React中绑定事件处理函数的常见方法,以及每种方法的优缺点。本文最重要的目的就是帮你找到那个“唯一的方法”。

▌在render()函数中动态绑定

第一种常用的方法就是在 .render() 函数中直接调用 .bind(this) 实现绑定。例如:

我们知道React使用Virtual DOM,每当绘制发生时,React会比较更新后的Virtual DOM和上一次的Virtual DOM,然后在画面上仅更新DOM树中改变了的部分。

在这个例子中,当 render() 被调用时,this.handleClick.bind(this) 也会被调用。注意:该调用会生成一个全新的绑定好的事件处理函数,这个函数与第一次 render() 被调用时的事件处理函数是不同的!

动态绑定情况下的Virtual DOM,蓝色的元素将被重绘

如上图所示,第一次 render() 被调用时,this.handleClick.bind(this) 返回 funcA,因此React认为 onChange 是 funcA。

之后当 render() 再次被调用时,this.handleClick.bind(this) 返回 funcB(注意它每次被调用都会返回一个新的函数)。于是,React认为 onChange 从 funcA 变成了 funcB,意味着button也会被重绘。

有人说就一个按钮其实无所谓了,但如果是100个按钮呢?

在上例中,任何 label 的改变都将导致所有的按钮被重绘(理想状况是只有label变更了的那个按钮才重绘,其他按钮不会),因为这个循环会为每个按钮生成新的 onChange 处理函数。

▌在constructor()中绑定

传统的做法是在构造函数中进行绑定。非常朴实的做法:

该方法要比第一种方法好得多。调用 render() 不会为 onClick 生成新的事件处理函数,因此只要按钮不发生变化, 就不会被重绘。

在构造函数中进行绑定情况下的Virtual DOM,蓝色的元素将被重绘

▌利用箭头函数绑定

通过ES7的类属性功能(现在可以通过Babel实现:https://babeljs.io/docs/plugins/transform-class-properties/),可以在定义方法的时候进行绑定:

上述代码中,handleClick 被赋值为绑定后的函数,这种写法相当于:

因此,在组件初始化结束之后,this.handleClick 的值就不会再变化。因此,这样做能保证 不会被重绘。个人认为这是实现绑定的最好的办法。代码写起来很简单、易懂,而且能达到我们的目的。

▌利用箭头函数为多个元素实现动态绑定

利用同样的技巧,可以用箭头函数给多个输入绑定同一个处理函数:

乍一看貌似没问题,而且实现极其简单明了。但仔细想想,就会发现这种方法与第一种方法有同样的问题:每次 render() 被调用时,两个 都会被重绘。

不过个人认为这种方法其实问题不大,毕竟实现简单,而且我不愿意为每个输入框都写一个 handleXXXChange 函数。而且最重要的是,这种“多用处理函数”几乎不太可能出现在一个列表中。也就是说,只会有固定数量的元素被重绘(而不像列表那样会产生任意多个),一般不会造成性能问题。

毕竟,这种写法带来的便利性要远远大于性能损失,所以我还是推荐这种做法的。

▌利用autobind自动绑定

还有一种方法是利用 lodash-decorators 或 react-autobind 等包,在定义类时自动绑定所有方法。我个人不太喜欢这种自动化处理,而是倾向于把绑定限制在必需的最小范围,但自动绑定的确能在保证代码可读性的同时节省很多工作量。代码大概类似于:

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180609A0Q8HZ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券