一文详解React事件中this指向,面试必备

结论

先上结论

  1. 4种处理事件处理函数中this指向问题的方案
  2. 3种实现事件传参的解决方案
  3. 5种this指向方案的性能比较
  4. 两种最优解

需求

我们知道,在 react 中,事件处理函数中的this很容易丢失,如

class App extends React.Component {
  handleClick() {
    console.log(this);// undefined 
  }
  render() {
    return <div onClick={this.handleClick} >点我</div>;
  }
}

4种this指向解决方案

结合原生 JavaScript 的理解,我们有如下4种解决方案

  1. 箭头函数
  2. 构造函数中的 bind
  3. 事件绑定时的 bind
  4. 事件绑定时的匿名函数+箭头函数。

下边我们分别给出它们的实现

箭头函数

写法上最简单

class App extends React.Component {
  // 修改为箭头函数
  handleClick = () => {
    console.log(this);// 正常 
  }
  render() {
    return <div onClick={this.handleClick} >点我</div>;
  }
}

构造函数中的bind

class App extends React.Component {
  constructor(props) {
    super();
    // 通过bind 改写this指向并返回新的函数
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    console.log(this);// 正常
  }
  render() {
    // bind
    return <div onClick={this.handleClick} >点我</div>;
  }
}

事件绑定时的bind

class App extends React.Component {
  handleClick() {
    console.log(this);// 正常
  }
  render() {
    // bind
    return <div onClick={this.handleClick.bind(this)} >点我</div>;
  }
}

事件绑定时的匿名函数+箭头函数

class App extends React.Component {
  handleClick() {
    console.log(this);// 正常
  }
  render() {
    // bind
    return <div onClick={() => this.handleClick()} >点我</div>;
  }
}

虽然以上四种方案都可以解决 事件处理函数中this指向的问题,但是由于我们在开发时,往往还需要做事件传参。

因此还有以下4种事件传参写法

3种事件传参解决方法

  1. 事件绑定时的 bind
  2. 事件绑定时的匿名函数+箭头函数
  3. 自定义属性 dataset

事件绑定时的bind

bind 函数不但可以修改this指向返回新函数,还可以接收参数。写法相当灵活飘逸

class App extends React.Component {
  handleClick(a, b, e) {
    console.log(this);// 正常
    console.log(a, b, e);// "过火","上火","事件对象"
  }
  render() {
    return <>
      <div onClick={this.handleClick.bind(this, "过火", "上火")} >点我</div>
    </>
  }
}

事件绑定时的匿名函数+箭头函数

class App extends React.Component {
  handleClick(a, b, e) {
    console.log(this);// 正常
    console.log(a, b, e);// "过火","上火","事件对象"
  }
  render() {
    return <>
      <div onClick={(event) => this.handleClick("过火", "上火", event)} >点我</div>
    </>
  }
}

自定义属性 dataset

class App extends React.Component {
  handleClick(e) {
    console.log(this);// 正常
    console.log(e.target.dataset.msg);// "过火"
  }
  render() {
    return <>
      <div data-msg="过火" onClick={this.handleClick.bind(this)} >点我</div>
    </>
  }
}

虽然 react 事件相关代码写法看起来多种多样,但是,如果从性能角度出发,写法就剩下以下一种。 事件绑定时的bind

5种this指向解决方案的性能比较

以下比较,主要从 实例成员函数能否得到复用触发,因为 类组件也可以理解是个class,那么我们可以将其函数成员做对比,看哪种方式是共享内存。

App

class App extends React.Component {
  constructor() {
    super();
    this.ref1 = React.createRef();
    this.ref2 = React.createRef();
  }

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn);
  }
  render() {
    return <>
      <div onClick={this.handleClick.bind(this)} >
        <Btn ref={this.ref1} />
        <Btn ref={this.ref2} />
      </div>
    </>
  }
}

Btn 不做this处理 true

没有处理this指向的问题

class Btn extends React.Component {
  showBtn() {
    console.log(this); // undefined
  }
  render() {
    return <button onClick={this.showBtn}>按钮</button>;
  }
}

这时 App

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn); // true
  }

Btn 箭头函数 false

class Btn extends React.Component {
  showBtn = () => {
    console.log(this);
  }
  render() {
    return <button onClick={this.showBtn}>按钮</button>;
  }
}

这时 App

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn);// false
  }

Btn 构造函数中的 bind false

class Btn extends React.Component {
  constructor() {
    super();
    this.showBtn = this.showBtn.bind(this);
  }
  showBtn() {
    console.log(this);
  }
  render() {
    return <button onClick={this.showBtn}>按钮</button>;
  }
}

这时 App

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn); // false
  }

Btn 事件绑定时的 bind true

class Btn extends React.Component {
  showBtn() {
    console.log(this);
  }
  render() {
    return <button onClick={this.showBtn.bind(this)}>按钮</button>;
  }
}

这时 App

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn); // true
  }

Btn 事件绑定时的匿名函数+箭头函数 true

class Btn extends React.Component {
  showBtn() {
    console.log(this);
  }
  render() {
    return <button onClick={() => this.showBtn()}>按钮</button>;
  }
}

这时 App

  handleClick(e) {
    //  比较 Btn 组件中的事件处理函数
    console.log(this.ref1.current.showBtn === this.ref2.current.showBtn); // true
  }

小结

结合以上的对比分析,推荐react中,事件处理函数的写法有2种

方式一

class Btn extends React.Component {
  showBtn() {
    console.log(this);
  }
  render() {
    return <button onClick={this.showBtn.bind(this)}>按钮</button>;
  }
}

方式二

class Btn extends React.Component {
  showBtn() {
    console.log(this);
  }
  render() {
    return <button onClick={() => this.showBtn()}>按钮</button>;
  }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值