Typescript:React事件类型

293

React事件的正确类型是什么?

最初,我只是为了简单起见使用了any。现在,我正在尝试清理代码并完全避免使用any

所以在这样一个简单的表单中:

export interface LoginProps {
  login: {
    [k: string]: string | Function
    uname: string
    passw: string
    logIn: Function
  }
}
@inject('login') @observer
export class Login extends Component<LoginProps, {}> {
  update = (e: React.SyntheticEvent<EventTarget>): void => {
    this.props.login[e.target.name] = e.target.value
  }
  submit = (e: any): void => {
    this.props.login.logIn()
    e.preventDefault()
  }
  render() {
    const { uname, passw } = this.props.login
    return (
      <div id='login' >
        <form>
          <input
            placeholder='Username'
            type="text"
            name='uname'
            value={uname}
            onChange={this.update}
          />
          <input
            placeholder='Password'
            type="password"
            name='passw'
            value={passw}
            onChange={this.update}
          />
          <button type="submit" onClick={this.submit} >
            Submit
          </button>
        </form>
      </div>
    )
  }
}

在这里我应该使用什么类型作为事件类型?

React.SyntheticEvent<EventTarget> 似乎不起作用,因为我会收到一个错误,说target上不存在namevalue

更一般化的答案适用于所有事件将不胜感激。

谢谢

11个回答

372

SyntheticEvent 接口是通用的:

interface SyntheticEvent<T> {
    ...
    currentTarget: EventTarget & T;
    ...
}

(从技术上讲,currentTarget属性位于父级BaseSyntheticEvent类型中。)

currentTarget是泛型约束和 EventTarget的交集。
此外,由于您的事件是由输入元素引起的,因此应使用ChangeEvent (定义文件React文档)。

应为:

update = (e: React.ChangeEvent<HTMLInputElement>): void => {
    this.props.login[e.currentTarget.name] = e.currentTarget.value
}

(注意:这个答案最初建议使用React.FormEvent。评论中的讨论与此建议有关,但应如上所示使用React.ChangeEvent。)

41
哪里可以找到关于常见事件类型的详细介绍?我找不到任何相关信息。 - r.sendecky
11
React事件类型?它们都在文档中的事件页面中描述。 - Nitzan Tomer
5
我收到了“name”不存在于类型“EventTarget&HTMLInputElements”(TS v2.4)的错误信息。 - Falieson
7
仍然出现以下错误。“在类型'EventTarget & HTMLInputElement'上不存在属性'value'。” - tomhughes
1
@CMCDragonkai 我认为e.key只是键盘事件的一部分。 - Nitzan Tomer
显示剩余11条评论

69

问题不在于Event类型,而是在于typescript中的EventTarget接口只有3个方法:

interface EventTarget {
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
    dispatchEvent(evt: Event): boolean;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
}

interface SyntheticEvent {
    bubbles: boolean;
    cancelable: boolean;
    currentTarget: EventTarget;
    defaultPrevented: boolean;
    eventPhase: number;
    isTrusted: boolean;
    nativeEvent: Event;
    preventDefault(): void;
    stopPropagation(): void;
    target: EventTarget;
    timeStamp: Date;
    type: string;
}

所以说,在EventTarget上确实不存在namevalue。你需要做的是将目标转换为具有所需属性的特定元素类型。在这种情况下,它将是HTMLInputElement

update = (e: React.SyntheticEvent): void => {
    let target = e.target as HTMLInputElement;
    this.props.login[target.name] = target.value;
}

对于事件处理程序,除了React.SyntheticEvent之外,您还可以将它们类型化为以下内容:EventMouseEventKeyboardEvent等,具体取决于处理程序的用例。

查看typescript和react中的.d.ts文件以查看所有这些类型定义的最佳方法。

此外,查看以下链接以获取更多解释:为什么在Typescript中Event.target不是Element?


2
看起来我的类型定义文件有点过时了,因为它没有显示通用的 SyntheticEvent<T> 接口。Nitzan Tomer 的答案更好。 - Edwin

50

我认为最简单的方法是:

type InputEvent = React.ChangeEvent<HTMLInputElement>;
type ButtonEvent = React.MouseEvent<HTMLButtonElement>;

update = (e: InputEvent): void => this.props.login[e.target.name] = e.target.value;
submit = (e:  ButtonEvent): void => {
    this.props.login.logIn();
    e.preventDefault();
}

48

为了结合Nitzan和Edwin的答案,我发现像这样做对我有用:

update = (e: React.FormEvent<EventTarget>): void => {
    let target = e.target as HTMLInputElement;
    this.props.login[target.name] = target.value;
}

3
this.props.login[target.name] = target.value; 这行代码是否改变了props的值呢? - Marwen Trabelsi
只是复制原问题中的这一行,以便答案对提问者有意义。 - cham

42

对于更新操作: event: React.ChangeEvent 对于提交操作: event: React.FormEvent 对于点击操作: event: React.MouseEvent


13

我在一个types.ts文件中定义了以下内容,用于HTML的输入、选择和文本区域:

export type InputChangeEventHandler = React.ChangeEventHandler<HTMLInputElement>
export type TextareaChangeEventHandler = React.ChangeEventHandler<HTMLTextAreaElement>
export type SelectChangeEventHandler = React.ChangeEventHandler<HTMLSelectElement>

然后将它们导入:

import { InputChangeEventHandler } from '../types'

然后使用它们:


const updateName: InputChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}
const updateBio: TextareaChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}
const updateSize: SelectChangeEventHandler = (event) => {
  // Do something with `event.currentTarget.value`
}

然后在您的标记上应用函数(将...替换为其他必要的属性):

<input onChange={updateName} ... />
<textarea onChange={updateName} ... />
<select onChange={updateSize} ... >
  // ...
</select>

2
我猜这取决于你想做什么(有点混乱),但对我来说,我必须将其更改为export type InputChangeEventHandler = React.ChangeEvent<HTMLInputElement>;才能使用event.target.value。使用ChangeEventHandler无法使用event.target,因为它是未定义的。 - Vadorequest

12

你可以在React中像这样做

handleEvent = (e: React.SyntheticEvent<EventTarget>) => {
  const simpleInput = (e.target as HTMLInputElement).value;
  //for simple html input values
  const formInput = (e.target as HTMLFormElement).files[0];
  //for html form elements
}

你的示例和handleEvent之间有什么区别:React.ReactEventHandler<HTMLInputElement> = (e) => { ... }。 - foreverAnIntern

9

以下是2022年TypeScript编译器的推荐使用方法:

const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
      console.log('click');
 };

不需要将void作为返回值添加,这是由编译器自动完成的。


3

对于那些正在寻找获取事件并在useState中存储某些东西(在我的情况下是HTML 5元素)的解决方案,这是我的解决方案:

const [anchorElement, setAnchorElement] = useState<HTMLButtonElement | null>(null);

const handleMenu = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) : void => {
    setAnchorElement(event.currentTarget);
};

0

以下具有相同的类型:

let event1: { target: { value: any } };
let event2: { target: HTMLInputElement } }; 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接