TS装饰器bindThis优雅实现React类组件中this绑定

news2024/9/19 11:04:31

初学React类组件时,最不爽的一点应该就是 this 指向问题了吧!初识React的时候,肯定写过这样错误的demo。

import React from 'react';
export class ReactTestClass extends React.Component {
  constructor(props) {
super(props);
this.state = { a: 1 };
  }
  handleClick() {
this.setState({a: 2})
  }
  render() {
return <div onClick={this.handleClick}>{this.state.a}</div>;
  }
}
上面的代码在执行 onClick 时,就会如期遇到如下的错误...

this 丢失了。编译React类组件时,会将 jsx 转成 React.createElement,并onClick 事件用对象包裹一层传参给该函数。

// 编译后的结果
class ReactTestClass extends _react.default.Component {
  constructor(props) {
super(props);
this.state = {
  a: 1
};
  }
  handleClick() {
this.setState({
  a: 2
});
  }
  render() {
return /*#__PURE__*/ _react.default.createElement(
  "div",
  {
onClick: this.handleClick // ❌ 鬼在这里
  },
  this.state.a
);
  }
}
exports.ReactTestClass = ReactTestClass;

 

写到这里肯定会让大家觉得是 React 在埋坑,其实不然,官方文档有澄清:

这并不是 React 自身的行为: 这是因为 函数在 JS 中就是这么工作的。通常情况下,比如onClick={this.handleClick},你应该 bind 这个方法。

经受过面向对象编程的洗礼,为什么还要在类中手动绑定 this? 我们参考如下代码、

class TestComponent {
logThis () {
console.log(this); // 这里的 `this` 指向谁?
}
privateExecute (cb) {
 cb();
}
execute () {
this.privateExecute(this.logThis); // 正确的情况应该传入 this.logThis.bind(this)
}
}
const instance = new TestComponent();
instance.execute();

上述代码如期打印了 undefined。就是在 privateRender 中执行回调函数(执行的是 logThis 方法)时,this 变成了 undefined。写到这里可能有人会提出疑问,就算不是类的实例调用的 logThis 方法,那 this 也应该是 window 对象。

没错!在非严格模式下,就是 window 对象,但是(知识点) 使用了 ES6 的 class 语法,所有在 class 中声明的方法都会自动地使用严格模式,故 this 就是 undefined

所以,在非React类组件内,有时候也得手动绑定 this

优雅的@bindThis

使用 .bind(this)

render() {
return <div onClick={this.handleClick.bind(this)}>{this.state.a}</div>;
}

或箭头函数

handleClick = () => {
this.setState({a: 2})
}

都可以完美解决,但是早已习惯面向对象和喜欢搞事情的我总觉得处理的不够优雅而大方。最终期望绑定this的方式如下,

import React from 'react';
import { bindThis } from './bind-this';
export class ReactTestClass extends React.Component {
  constructor(props) {
super(props);
this.state = { a: 1 };
  }
  @bindThis // 通过 `方法装饰器` 自动绑定this
  handleClick() {
this.setState({ a: 2 });
  }
  render() {
return <div onClick={this.handleClick}>{this.state.a}</div>;
  }
}

对于 方法装饰器,该函数对应三个入参,依次是

export function bindThis(
target: Object, 
propertyKey: string, 
descriptor: PropertyDescriptor,
) {
// 如果要返回值,应返回一个新的属性描述器
}

target 对应的是类的 prototype

propertyKey 对应的是方法名称,字符串类型,例如 "handleClick"

descriptor 属性描述器

对于 descriptor 能会比较陌生,当前该属性打印出来的结果是,

{
  value: [Function: handleClick],
  writable: true,
  enumerable: false,
  configurable: true
}

参看 MDN 上的 Object.defineProperty,我们发现对于属性描述器一共分成两种,data descriptor 和 accessor descriptor,两者的区别主要在内在属性字段上:

✅ 可以存在的属性,❌ 不能包含的属性

其中,

configurable,表示两种属性描述器能否转换、属性能否被删除等,默认 false

enumerable,表示是否是可枚举属性,默认 false

value,表示当前属性值,对于类中 handleClick 函数,value就是该函数本身

writable,表示当前属性值能否被修改

get,属性的 getter 函数。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)

set,属性的 setter 函数。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的this对象。

既然 get 函数有机会传入 this 对象,我们就从这里入手,通过 @bindThis 装饰器给 handleClick 函数绑定真正的 this

export function bindThis(
target: Object, 
propertyKey: string, 
descriptor: PropertyDescriptor,
) {
const fn = descriptor.value; // 先拿到函数本身
return {
configurable: true,
get() {
const bound = fn.bind(this); // 这里的 this 是当前类的示例
return bound;
}
}
}

bingo~~~

一个优雅又不失功能的 @bindThis 装饰器就这么愉快地搞定了。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员如何进化成架构师?

作为程序员的上端&#xff0c;每一个架构师都承担着艰巨的任务&#xff1a;不仅仅要和产品进行周旋&#xff0c;还需要有十分坚实的技术作为基础打底。 那么&#xff0c;架构师都在考虑什么呢&#xff1f; 说到底还是程序架构、结构性的问题。 比如最近十分火爆的分布式系统…

STM32CubeIDE(stm32f767)添加DSP库

对于stm32f4 系列可以使用STM32Cube添加相应的库&#xff0c;自动生成代码。可以参考下面写的文章 (1条消息) STM32CubeMX关于添加DSP库的使用_W_oilpicture的博客-CSDN博客_cubemx dsp库 不过&#xff0c;对于stm32f767等M7的内核目前可能并不适用&#xff0c;需要自己手动添…

CS162 shell

本文记录我在做shell这个作业时用到有关资源&#xff0c;如Linux系统调用、Linux基础知识、C语言知识等。 这里只是非常简略地记录了一下&#xff0c;并且可能有理解不正确的地方&#xff0c;你可以把本文当作一个索引和没有思路时的启发&#xff0c;详细的信息可以再去查&…

SVM(支持向量机)基本形式推导

据说在dl之前是SVM撑起了ml的半片天&#xff0c;学习后发现SVM是由纯粹的数学推导、转化、求解、优化“堆砌”而来&#xff0c;不如说是数学撑起了ml&#xff0c;ml是数学的学科。以下根据老师ppt上讲解的思路讲讲个人对SVM基本形式推导的理解。 margin&#xff08;间隔&#x…

[附源码]计算机毕业设计现代诗歌交流平台Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2. IMU原理及姿态融合算法详解

文章目录2. IMU原理及姿态融合算法详解一、组合二、 原理a) 陀螺仪b) 加速度计c) 磁力计三、 旋转的表达a) 欧拉角b) 旋转矩阵c) 四元数d) 李群 SO(3)\text{SO}(3)SO(3) 及 李代数 so(3)\text{so}(3)so(3)四、 传感器的噪声及去除a) 陀螺仪b) 加速度计c) 磁力计五、姿态解算原理…

CSS基础-选择器进阶,背景相关属性(颜色/图片)

CSS基础-选择器进阶,背景相关属性(颜色/图片) 目标&#xff1a;能够理解 复合选择器 的规则&#xff0c;并使用 复合选择器 在 HTML 中选择元素 学习路径&#xff1a;1. 复合选择器2. 并集选择器3. 交集选择器4. hover伪类选择器5. Emmet语法 本次我们所学的内容&#xff1a; 1…

Kafka - 15 Kafka Offset | 自动和手动提交Offset | 指定Offset消费 | 漏消费和重复消费 | 消息积压

文章目录1. Offset 的默认维护位置2. 自动提交 Offset3. 手动提交 Offset1. 同步提交 offset2. 异步提交 offset4. 指定 Offset 消费5. 指定时间消费6. 漏消费和重复消费7. 消费者事务8. 数据积压&#xff08;消费者如何提高吞吐量&#xff09;1. Offset 的默认维护位置 Kafka…

触发器——SR锁存器

组合逻辑的基本单元电路是门电路 另外一种电路叫做时序逻辑电路&#xff0c;时序逻辑电路的输出不但和输入有关&#xff0c;还和原来的状态有关 在这样的电路中&#xff0c;一定要具有存储功能&#xff0c;存储原来的状态&#xff0c;一定也要有反馈回路&#xff0c;返回原来…

4-7:用Redis优化登陆模块

相关功能 使用Redis存储验证码 验证码需要频繁的访问与刷新&#xff0c;对性能要求较高。验证码不需永久保存&#xff0c;通常在很短的时间后就会失效。 (Redis可以设置有效时间&#xff0c;分布式应用也可以绕过session共享的问题)分布式部署时&#xff0c;存在Session共享的…

2022物联卡排行榜公司有哪些?

科技的发展日新月异&#xff0c;我们国家的发展战略也是支持高新科技公司的发展&#xff0c;所以越来越多的高新科技公司出现&#xff0c;但凡是高新科技公司&#xff0c;在设备的联网中&#xff0c;都会用到物联卡&#xff0c;所以物联卡的市场也愈发火爆&#xff0c;那么今天…

Zygote在Framework中起什么作用?

前言 提到Zygote可能了解一些的小伙伴会说&#xff0c;它是分裂进程用的。没错它最大的作用的确是分裂进程&#xff0c;但是它除了分裂进程外还做了什么呢。还是老规矩&#xff0c;让我们抱着几个问题来看文章。最后在结尾&#xff0c;再对问题进行思考回复。 你能大概描述一…

【springboot进阶】使用aop + 注解方式,简单实现spring cache redis 功能

目录 一、实现思路 二、定义缓存注解 三、aop 切面处理 四、使用方式 五、灵活的运用 六、总结 前几天有同学看了 SpringBoot整合RedisTemplate配置多个redis库 这篇文章&#xff0c;提问spring cache 能不能也动态配置多个redis库。介于笔者没怎么接触过&#xff0c;所以…

【Graph】NetworkX官方基础教程

NetworkX官方基础教程图的基础知识1.1 图&#xff08;graph&#xff09;及其分类1.2 节点的度&#xff08;degree&#xff09;1.3 子图&#xff08;subgraph&#xff09;1.4 连通图1.5 图的矩阵表示NetworkX概述NetworkX基础教程1. 创建图2. 节点3. 边4. 清空图5. 图可视化6. 访…

基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档

在互联网高速发展、信息技术步入人类生活的情况下&#xff0c;电子贸易也得到了空前发展。网购几乎成为了人人都会进行的活动。近几年来&#xff0c;养宠物更是成为人们生活中重要的娱乐内容之一&#xff0c; 人们越来越多的讲感情也寄托给了宠物&#xff0c;以给自己另一个感情…

自动驾驶--预测技术

根据百度技术培训中心课程整理( https://bit.baidu.com/productsBuy?id72) 背景简介 无人车系统从算法模块可分为三个部分&#xff0c;首先是感知通过对传感器数据和环境信息进行计算来解决周围有什么的问题&#xff0c;其次是预测&#xff0c;根据感知信息预测环境下一步将…

Java单元测试

1. 序言 1.1 工作中要求进行单元测试 毕业进入公司时&#xff0c;为了锻炼笔者的Java基础&#xff0c;老大给笔者分配了平台化开发的工作&#xff0c;基于Spring Boot Mybatis的Java Web后端开发一个人干后端开发&#xff0c;且以前也没有后端开发的经验&#xff0c;所以只是…

CTF之序列化__toString

序列化简介 本质上serialize()和unserialize&#xff08;&#xff09;在php内部的实现上是没有漏洞的&#xff0c;漏洞的主要产生是由于应用程序在处理对象&#xff0c;魔术函数以及序列化相关问题时导致的。 当传给unserialize()的参数可控时&#xff0c;那么用户就可以注入精…

【应用】Docker Swarm

Docker SwarmDocker Swarm 集群配置配置前准备初始化 SwarmSwarm 常用命令Portainer 集群管理Docker Swarm 集群配置 masternode1node2192.168.86.133192.168.86.131192.168.86.139 配置前准备 关闭各个节点服务器的防火墙 systemctl stop firewalld systemctl disable fire…