TS_React:类型化EventHandler

焦虑可分为有用焦虑无用焦虑两种。

  1. 有用焦虑指向现在
  2. 无用焦虑指向未来,它的本质,是对现在失控的恐惧

大家好,我是柒八九

今天还是--TypeScript实战系列的文章。前面的文章中,我们从不同的角度介绍了,TS是如何结合React进行项目开发的。相关文章如下。

而今天我们主要是讲如何利用TSReact中的事件回调进行类型化处理。

好了,天不早了。我们开始粗发


1. 示例代码

这是一个非常简单的React应用,有一个input和一个button。我们用这个例子来一步步处理,如何用TS处理里面的事件回调。

import { useState } from 'react';

export default function App() {
  const [inputValue, setInputValue] = useState('');

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  const handleClick = (event) => {
    console.log('提交被触发');
  };

  return (
    <div className="App">
      <h1>前端柒八九</h1>
      <input value={inputValue} onChange={handleInputChange} />
      <button onClick={handleClick}>提交</button>
    </div>
  );
}

2. 添加TS

有几种方法来类型化上述代码中的回调函数,我们将看到3种主要的方法。

  1. 类型化事件处理程序的参数
  2. 类型化事件处理程序本身
  3. 依靠类型推断

类型化事件处理程序的参数(event)

先处理onClick事件。React 提供了一个 MouseEvent 类型,可以直接使用!

import { 
    useState, 
+   MouseEvent,
} from 'react';

export default function App() {
    
  // 省略部分代码
  
+  const handleClick = (event: MouseEvent) => {
    console.log('提交被触发');
  };

  return (
    <div className="App">
      <h1>前端柒八九</h1>
      
      <button onClick={handleClick}>提交</button>
    </div>
  );
}

onClick 事件实际上是由React维护的:它是一个合成事件
合成事件是React浏览器事件的一种包装,以便不同的浏览器,都有相同的API

handleInputChange函数与 handleClick 非常相似,但有一个明显的区别。不同的是,ChangeEvent 是一个泛型,你必须提供什么样的DOM元素正在被使用

import { 
    useState, 
+   ChangeEvent
} from 'react';

export default function App() {
  const [inputValue, setInputValue] = useState('');

+ const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };

  // 省略部分代码

  return (
    <div className="App">
      <h1>前端柒八九</h1>
      <input value={inputValue} onChange={handleInputChange} />
      
    </div>
  );
}

在上面的代码中需要注意的一点是,HTMLInputElement 特指HTML的输入标签。如果我们使用的是 textarea,我们将使用 HTMLTextAreaElement 来代替。

注意,MouseEvent 也是一个泛型,你可以在必要时对它进行限制。例如,让我们把上面的 MouseEvent 限制为专门从一个按钮发出的鼠标事件。

const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
  console.log('提交被触发');
};

还需要提示的是,React为我们提供了很多 Event 对象的类型声明。

Event 事件对象类型

事件类型 解释
ClipboardEvent<T = Element> 剪切板事件对象
DragEvent<T =Element> 拖拽事件对象
ChangeEvent<T = Element> Change事件对象
KeyboardEvent<T = Element> 键盘事件对象
MouseEvent<T = Element> 鼠标事件对象
TouchEvent<T = Element> 触摸事件对象
WheelEvent<T = Element> 滚轮时间对象
AnimationEvent<T = Element> 动画事件对象
TransitionEvent<T = Element> 过渡事件对象

类型化事件处理程序本身

React 声明文件所提供的 EventHandler 类型别名,通过不同事件的 EventHandler 的类型别名来定义事件处理函数的类型,更方便定义其函数类型。

type EventHandler<E extends SyntheticEvent<any>> = {

  bivarianceHack(event: E): void

}['bivarianceHack']

bivarianceHack 为事件处理函数的类型定义,函数接收一个 event 对象,并且其类型为接收到的泛型变量 E 的类型, 返回值为 void

而在类型定义的时候,有一个很怪异的行为['bivarianceHack']

这与 strictfunctionTypes 下的功能兼容性有关。在此选项下,如果参数是派生类型,则不能将其传递给将传入基类参数的函数。例如:

class Animal { private x:undefined }
class Dog extends Animal { private d: undefined }

type EventHandler<E extends Animal> = (event: E) => void

let o: EventHandler<Animal> = (o: Dog) => { } // 在 strictFunctionTypes 模式下,失败

此时,TS会报警告。

所以hack的作用是即使在 strictFunctionTypes启用的情况下允许EventHandler的二元行为。 由于事件处理程序的签名将在方法声明中有其来源,因此它不会受到更严格的函数检查。

type BivariantEventHandler<E extends Animal> = { bivarianceHack(event: E): void }["bivarianceHack"];
// 在 strictFunctionTypes 模式下,生效
let o2: BivariantEventHandler<Animal> = (o: Dog) => { } 

讲的有点多,我们还是绕回本文的重点。使用EventHandler来对上面的例子进行改造处理。

import { 
   useState, 
+  ChangeEventHandler, 
+  MouseEventHandler 
} from 'react';

export default function App() {
  const [inputValue, setInputValue] = useState('');

 
+ const handleInputChange: ChangeEventHandler<HTMLInputElement> = (event) =>{
    setInputValue(event.target.value);
  };

+  const handleClick: MouseEventHandler = (event) => {
    console.log('提交被触发');
  };

  return (
   // ...省略....
  );
}

系不系,很简单。


依赖类型推断

你也可以依靠类型推断,而不需要自己处理函数。但是,你需要将回调函数内联处理

import { useState } from 'react';

export default function App() {
  const [inputValue, setInputValue] = useState('');

  return (
    <div className="App">
      <h1>前端柒八九</h1>
      <input 
        value={inputValue} 
+        onChange={(event) => setInputValue(event.target.value)}
      />
      <button 
+        onClick={(event) => console.log('提交被触发')}
      >
        提交
      </button>
    </div>
  );
}

这个更简单


后记

分享是一种态度

参考资料:

全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。

本文由mdnice多平台发布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容