
带你找出react中,回调函数绑定this最完美的写法!(文末 有惊喜)
相信每一个人写过
react的人都对react组件的的this绑定有或多或少的了解
在我看来,有若干种this写法,我们通过本文,一步步找优缺点,筛选出最完美的react this写法!(有点小激动)
1、远古时代 React.createClass
说实话,在我接触react的时候,这种写法就只在相关文章见到了。React.createClass会自动绑定所有函数的this到组件上
1 | React.createClass({ |
react 0.13开始就已经支持class声明组件了。react 16已经废弃了这种写法,这里就不讨论了。直接淘汰
2、错误示范
1 | class App extends React.Component { |
这种写法,最终打印this是指向undefined。原因在于上面的事件绑定函数调用可以看作如下。
1 | // 伪代码 |
在onClick进行调用时,this的上下文是全局,由于是在es module中,全局this指向undefined,所以这个错误示范的事件处理函数中的this不是指向组件本身的
3、利用proposal-class-public-fields直接绑定箭头函数
1 | class App extends React.Component { |
目前
proposal-class-public-fields仍处于提案阶段,需要借助@babel/plugin-proposal-class-properties这个 babel 插件在浏览器中才能正常工作
经过babel转换,等价于以下的代码
1 | class App extends React.Component { |
可以看出,3和2从最大的区别在于,3将fn直接绑定在实例的属性上(2是绑定在原型的方法上),并利用箭头函数继承父级this作用域达到了this绑定的效果。
优点:代码十分简洁,不需要手动写bind、也不需要在constructor中进行额外的操作
缺点:很多文章都提到这是一种完美写法,但其实每一个实例在初始化的时候都会新建一个新事件回调函数(因为绑定在实例的属性上,每个实例都有一个fn的方法。本质上,这是一种重复浪费),所以其实并不是很完美
4、Constructor中使用 bind
1 | class App extends React.Component { |
优点:几乎等价于3
缺点:代码写起来比较繁琐,需要在constructor中,手动绑定每一个回调函数
5、在render中进行bind绑定
1 | class App extends React.Component { |
优点:fn函数多次实例化只生成一次,存在类的属性上,类似于4,写法上比4稍微好一点。
缺点:this.fn.bind(this)会导致每次渲染都是一个全新的函数,在使用了组件依赖属性进行比较、pureComponent、函数组件React.memo的时候会失效。
最关键的是5的写法会被6全方面吊打完爆
6、箭头函数内联写法
1 | class App extends React.Component { |
优点:
1、写法简洁
2、与2-5的写法相比,6写法最大的最大好处就是传参灵活
3、全面吊打写法4,相同的缺点,但是多了传参数灵活。如果需要渲染一个数组,并且数组根据不同项,事件处理不一样时,2-5就很尴尬了
1 | const arr = [1, 2, 3, 4, 5]; |
网上看多文章都在使用3的方案的时候推荐使用闭包传参实现该效果
1 | const arr = ["1", "2", "3", "4", "5"]; |
经过前面的分析。使用这种写法,还不如直接使用6的内联写法,两种每次都是返回全新的函数,而且,少了一次返回闭包函数的开销。
缺点: 每次渲染都是一个全新的函数,类似于5的缺点,在使用了组件依赖属性进行比较、pureComponent、函数组件React.memo的时候会失效
7、函数组件的useCallback
虽然函数组件无this一说法,但既然讲到react回调函数,还是提一下
在hook出现之前,函数组件是不能保证每次的回调函数都是同一个的,(虽然可以把回调提到函数作用域外固定,但都是一些 hack 的方法了)
1 | const App = () => { |
有了hook。我们便可以使用useCallback固定住回调
1 | const App = () => { |
有没有发现。其实很类似class组件的将回调挂在class上,嗯,这就hook强大的地方,利用了react fiber,挂在了它的memorizeState上,实现了能在多次渲染中保持(这就不展开讲了)。缺点还是和上面提过的,参数传递不方便,如渲染数组
8、(最完美)的写法?
当然,如果不使用内联写法又获取到参数行不行呢。当然也是可以的,利用元素的自定义属性data-属性传递参数
1 | const arr = ["1", "2", "3", "4", "5"]; |
orz! 这是最完美写法了吧!不考虑代码繁琐的情况下,既正确绑定了this,又不会多次实例化函数,又能渲染数组。。
其实还是错误的…data-xxx属性只能传递string类型的数据,因为是附加给html的,react会进行一步JSON.stringify的操作,如果你传递一个对象,打印出来是value: "[object Object]"。果然,就算是为了获取字符串参数,也不推荐这种写法。可以,**但没必要!**
9、最省事的写法?
有一位大佬写了一个 babel 插件babel-plugin-react-scope-binding的插件,能够实现 将2的错误示范自动转化内联函数,更牛逼的是还能传参。介绍。确实是最省事的写法,不过很容易引起歧义,也有上面提到的问题
好吧,感谢你看到这里,废话连篇一篇文章,其实似乎并没有找回完美的写法。。。
下面说说本人的一些愚见吧
在平时写代码中,在render没有非常大的开销情况下(也没有依赖组件的某些属性进行性能优化、没使用 pureComponent), 会优先使用纯内联的写法(无论是函数组件还是 class 组件)。因为重新创建函数开销我觉得不是特别大的,并且内联我觉得还有最大的好处就是,看到一个事件调用,不需要再点到事件函数调用的地方…减少了飞来飞去的情况,而且上面也提到,内联传递参数是非常方便的。在实在遇到性能问题,再考虑优化。无需为了优化而优化
最近春招季,看完这篇文章,虽然还是找不出最完美的react绑定事件写法,但是面试官提起react绑定事件的几种区别时,相信大家都能答出来了。。。。
- Post title:带你找出react中,回调函数绑定this最完美的写法!
- Post author:flytam
- Create time:2020-03-09 20:18:09
- Post link:https://blog.flytam.vip/带你找出react中,回调函数绑定this最完美的写法!.html
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.