Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >带你找出react中,回调函数绑定this最完美的写法!

带你找出react中,回调函数绑定this最完美的写法!

原创
作者头像
ACK
发布于 2020-03-10 01:52:05
发布于 2020-03-10 01:52:05
1.6K0
举报

相信每一个人写过react的人都对react组件的的this绑定有或多或少的了解

在我看来,有若干种this写法,我们通过本文,一步步找优缺点,筛选出最完美的react this写法!(有点小激动)

1、远古时代 React.createClass

说实话,在我接触react的时候,这种写法就只在相关文章见到了。React.createClass会自动绑定所有函数的this到组件上

代码语言:txt
AI代码解释
复制
React.createClass({
    fn() {
        //  this 指向组件本身
        console.log(this);
    },
    render() {
        return <div onClick={this.fn}></div>;
    }
});

react 0.13开始就已经支持class声明组件了。react 16已经废弃了这种写法,这里就不讨论了。直接淘汰

2、错误示范
代码语言:txt
AI代码解释
复制
class App extends React.Component {
    fn() {
        console.log(this);
    }
    render() {
        return <div onClick={this.fn}></div>;
    }
}

这种写法,最终打印this是指向undefined。原因在于上面的事件绑定函数调用可以看作如下。

代码语言:txt
AI代码解释
复制
// 伪代码
onClick = app.fn;
onClick();

onClick进行调用时,this的上下文是全局,由于是在es module中,全局this指向undefined,所以这个错误示范的事件处理函数中的this不是指向组件本身的

3、利用proposal-class-public-fields直接绑定箭头函数
代码语言:txt
AI代码解释
复制
class App extends React.Component {
    fn = () => {
        console.log(this);
    };
    render() {
        return <div onClick={this.fn}></div>;
    }
}

目前proposal-class-public-fields仍处于提案阶段,需要借助@babel/plugin-proposal-class-properties这个 babel 插件在浏览器中才能正常工作

经过babel转换,等价于以下的代码

代码语言:txt
AI代码解释
复制
class App extends React.Component {
    constructor(props) {
        super(props);
        this.fn = () => {
            console.log(this);
        };
    }
    render() {
        return <div onClick={this.fn}></div>;
    }
}

可以看出,32从最大的区别在于,3fn直接绑定在实例的属性上(2是绑定在原型的方法上),并利用箭头函数继承父级this作用域达到了this绑定的效果。

优点:代码十分简洁,不需要手动写bind、也不需要在constructor中进行额外的操作

缺点:很多文章都提到这是一种完美写法,但其实每一个实例在初始化的时候都会新建一个新事件回调函数(因为绑定在实例的属性上,每个实例都有一个fn的方法。本质上,这是一种重复浪费),所以其实并不是很完美

4、Constructor中使用 bind
代码语言:txt
AI代码解释
复制
class App extends React.Component {
    constructor(props) {
        super(props);
        this.fn = this.fn.bind(this);
    }
    fn() {
        console.log(this);
    }
    render() {
        return <div onClick={this.fn}></div>;
    }
}

优点:fn函数在组件多次实例化过程中只生成一次(因为是用实例的fn属性直接指向了组件的原型,并绑定了this属性)

缺点:代码写起来比较繁琐,需要在constructor中,手动绑定每一个回调函数

5、在render中进行bind绑定
代码语言:txt
AI代码解释
复制
class App extends React.Component {
    fn() {
        console.log(this);
    }
    render() {
        return <div onClick={this.fn.bind(this)}></div>;
    }
}

优点:fn函数多次实例化只生成一次,存在类的属性上,类似于4,写法上比4稍微好一点。

缺点:this.fn.bind(this)会导致每次渲染都是一个全新的函数,在使用了组件依赖属性进行比较、pureComponent、函数组件React.memo的时候会失效。

最关键的是5的写法会被6全方面吊打完爆

6、箭头函数内联写法
代码语言:txt
AI代码解释
复制
class App extends React.Component {
    fn() {
        console.log(this);
    }
    render() {
        return <div onClick={() => fn()}></div>;
    }
}

优点:

1、写法简洁

2、与2-5的写法相比,6写法最大的最大好处就是传参灵活

3、全面吊打写法4,相同的缺点,但是多了传参数灵活。如果需要渲染一个数组,并且数组根据不同项,事件处理不一样时,2-5就很尴尬了

代码语言:txt
AI代码解释
复制
const arr = [1, 2, 3, 4, 5];
class App extends React.Component {
    fn(val) {
        console.log(val);
    }
    render() {
        return (
            <div>
                {arr.map(item => (
                    // 采用 6的写法,要打印数组这一项就很方便
                    <button onClick={() => this.fn(item)}>{item}</button>
                ))}
            </div>
        );
    }
}

网上看多文章都在使用3的方案的时候推荐使用闭包传参实现该效果

代码语言:txt
AI代码解释
复制
const arr = ["1", "2", "3", "4", "5"];
class App extends React.Component {
    fn = val => () => {
        console.log(val);
    };
    render() {
        return (
            <div>
                {arr.map(item => (
                    // 每次也生成了全新的函数了
                    <button onClick={this.fn(item)}>{item}</button>
                ))}
            </div>
        );
    }
}

经过前面的分析。使用这种写法,还不如直接使用6的内联写法,两种每次都是返回全新的函数,而且,少了一次返回闭包函数的开销。

缺点: 每次渲染都是一个全新的函数,类似于5的缺点,在使用了组件依赖属性进行比较、pureComponent、函数组件React.memo的时候会失效

7、函数组件的useCallback

虽然函数组件无this一说法,但既然讲到react回调函数,还是提一下

hook出现之前,函数组件是不能保证每次的回调函数都是同一个的,(虽然可以把回调提到函数作用域外固定,但都是一些 hack 的方法了)

代码语言:txt
AI代码解释
复制
const App = () => {
    // 每次都是全新的
    return <div onClick={() => console.log(2333)}></div>;
};

有了hook。我们便可以使用useCallback固定住回调

代码语言:txt
AI代码解释
复制
const App = () => {
    const fn = useCallback(() => console.log(2333), []);
    // 每次都是固定
    return <div onClick={fn}></div>;
};

有没有发现。其实很类似class组件的将回调挂在class上,嗯,这就hook强大的地方,利用了react fiber,挂在了它的memorizeState上,实现了能在多次渲染中保持(这就不展开讲了)。缺点还是和上面提过的,参数传递不方便,如渲染数组

8、(最完美)的写法?

当然,如果不使用内联写法又获取到参数行不行呢。当然也是可以的,利用元素的自定义属性data-属性传递参数

代码语言:txt
AI代码解释
复制
const arr = ["1", "2", "3", "4", "5"];
class App extends React.Component {
    constructor(props) {
        super(props);
        this.fn = this.fn.bind(this);
    }
    fn(e) {
        // 1 2 3 4 5
        console.log(e.target.dataset.val);
    }
    render() {
        return (
            <div>
                {arr.map(item => (
                    // 每次也生成了全新的函数了
                    <button data-value={item} onClick={this.fn}>
                        {item}
                    </button>
                ))}
            </div>
        );
    }
}

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绑定事件的几种区别时,相信大家都能答出来了。。。。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
React两大组件,三大核心属性,事件处理和函数柯里化
3.我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界。
大忽悠爱学习
2021/11/15
3.1K0
react面试题合集
Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 就是一个简单的 javascript 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该有任何副作用。
beifeng1996
2022/12/12
6590
React 深入系列5:事件处理
文:徐超,《React进阶之路》作者 授权发布,转载请注明作者及出处 ---- React 深入系列5:事件处理 React 深入系列,深入讲解了React中的重点概念、特性和模式等,旨在帮助大家加深对React的理解,以及在项目中更加灵活地使用React。 Web应用中,事件处理是重要的一环,事件处理将用户的操作行为转换为相应的逻辑执行或界面更新。在React中,处理事件响应的方式有多种,本文将详细介绍每一种处理方式的用法、使用场景和优缺点。 使用匿名函数 先上代码: //代码1 class
iKcamp
2018/05/28
6660
(转载非原创)React事件绑定的方式
在react应用中,事件名都是用小驼峰格式进行书写,例如onclick要改写成onClick
xlj
2021/07/23
3560
React: 事件处理和绑定方法
注意要显式调用 bind(this) 将事件函数上下文绑定要组件实例上,这也是 React 推崇的原则:没有黑科技,尽量使用显式的容易理解的 JavaScript 代码。
西南_张家辉
2021/02/02
1.1K0
react中类组件传值,函数组件传值:父子组件传值、非父子组件传值[通俗易懂]
函数组件中我们一般情况下使用useEffect实现数据的请求 // useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount (加载,更新,卸载)这三个函数的组合。 语法格式: useEffect(()=>{ },[])
全栈程序员站长
2022/08/30
6.5K0
React基础
React起源于FaceBook的内部项目,用来架设Instagram的网站,并于2013年5月开源。
小二丶
2023/11/28
1.4K1
React组件绑定this的四种方式
用react进行开发组件时,我们需要关注一下组件内部方法this的指向,react定义组件的方式有两种,一种为函数组件,一种为类组件,类组件内部可以定义一些方法,这些方法的this需要绑定到组件实例上,小编这里总结了一下,一共有四种方案:
挥刀北上
2019/07/19
5020
一文详解React事件中this指向,面试必备
虽然以上四种方案都可以解决 事件处理函数中this指向的问题,但是由于我们在开发时,往往还需要做事件传参。
万少
2025/02/11
760
细说React组件性能优化
React 组件性能优化的核心是减少渲染真实 DOM 节点的频率,减少 Virtual DOM 比对的频率。如果子组件未发生数据改变不渲染子组件。
xiaofeng123aa
2022/10/18
1.4K0
React 开发要知道的 34 个技巧
原理:子组件里面利用 props 获取父组件方法直接调用,从而改变父组件的值 注意: 此方法和 props 大同小异,都是 props 的应用,所以在源码中没有举例
前端老王
2020/09/23
1.5K0
照着官方文档学习react
准备 先要准备环境。搭建一个基于webpack的react环境:Hello ReactJS. 一些要点 我在想是否应该完整的记录照抄的过程呢。毕竟已经开始一段,前面的要不要补上?回头看以前写过的angularJS的博客,现在完全不会了,太久没用了。所以,还是记录基础以及关注的问题就好。 1.1 基本格式 react的模板文件后缀结尾为.jsx。 react可以采用html标签拼接的方式定义一个元素。比如: const element = <h1>Hello, world</h1>; 假设页面有个div: <
Ryan-Miao
2018/03/14
2.9K0
照着官方文档学习react
新手学习 react 迷惑的点(完整版)
网上各种言论说 React 上手比 Vue 难,可能难就难不能深刻理解 JSX,或者对 ES6 的一些特性理解得不够深刻,导致觉得有些点难以理解,然后说 React 比较难上手,还反人类啥的,所以我打算写两篇文章来讲新手学习 React 的时候容易迷惑的点写出来,如果你还以其他的对于学习 React 很迷惑的点,可以在留言区里给我留言。
桃翁
2019/09/09
1.2K0
新手学习 react 迷惑的点(完整版)
React如何处理事件
以上示例在点击链接时,会报错:Uncaught TypeError: Cannot read property 'setState' of undefined。因为this指向的并不是该组件。所以为了能方便调用当前组件的其他属性或方法,需要将this指向为当前实例
用户1272076
2019/03/27
9100
一道React面试题把我整懵了
提问:react项目中的JSX里,onChange={this.func.bind(this)}的写法,为什么要比非bind的func = () => {}的写法效率高?
beifeng1996
2022/09/30
1.2K0
一天梳理完React所有面试考察知识点
在shouldComponentUpdate()判断中,有一个有意思的问题,解释为什么 React setState() 要用不可变值
beifeng1996
2022/10/06
2.8K0
【React】归纳篇(四)组件的三大属性之 state | props | refs 属性
前端修罗场
2023/10/07
2510
React 开发必须知道的 34 个技巧【近1W字】
React 是前端三大框架之一,在面试和开发中也是一项技能; 本文从实际开发中总结了 React 开发的一些技巧技巧,适合 React 初学或者有一定项目经验的同学; 万字长文,建议收藏。 序列文章:Vue 开发必须知道的 36 个技巧【近1W字】
火狼1
2019/11/13
3.6K0
React基础
需要为遍历项指定一个不重复的key key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
P轴
2022/11/18
1.7K0
React基础
React学习笔记(二)—— JSX、组件与生命周期
JSX = JavaScript XML,这是React官方发明的一种JS语法(糖)
张果
2023/03/01
5.9K0
React学习笔记(二)—— JSX、组件与生命周期
相关推荐
React两大组件,三大核心属性,事件处理和函数柯里化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档