Typescript: React event types
Asked Answered
W

11

304

What is the correct type for React events?

Initially I just used any for the sake of simplicity. Now, I am trying to clean things up and avoid use of any completely.

So in a simple form like this:

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>
    )
  }
}

What type do I use here as event type?

React.SyntheticEvent<EventTarget> does not seem to be working as I get an error that name and value do not exist on target.

More generalised answer for all events would be really appreciated.

Thanks

Womack answered 7/2, 2017 at 4:6 Comment(0)
L
382

The SyntheticEvent interface is generic:

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

(Technically the currentTarget property is on the parent BaseSyntheticEvent type.)

And the currentTarget is an intersection of the generic constraint and EventTarget.
Also, since your events are caused by an input element you should use the ChangeEvent (in definition file, the react docs).

Should be:

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

(Note: This answer originally suggested using React.FormEvent. The discussion in the comments is related to this suggestion, but React.ChangeEvent should be used as shown above.)

Latoyialatreece answered 7/2, 2017 at 9:13 Comment(16)
What is a good spot to read about all the common event types? Unable to find anywhere.Womack
React event types? They are all described in the events page in the docs.Latoyialatreece
I get 'name' does not exist on type 'EventTarget & HTMLInputElements' (TS v2.4)Relaxation
@Relaxation Weird. If you do let obj: HTMLInputElement; console.log(obj.name) is it ok or do you get an error?Latoyialatreece
This still gives me the following error. "Property 'value' does not exist on type 'EventTarget & HTMLInputElement'."Pasadis
@Pasadis what is the version of the react.d.ts that you are using?Latoyialatreece
@NitzanTomer Using React 16.2 with @types/react 16.0.40. Both are the latest version of their respective packages, should the version numbers match?Pasadis
@Pasadis I do not get this error (with the code in my answer: update = (e: React.FormEvent<HTMLInputElement)...) using the same version of @types/react. I'll need to see your code.Latoyialatreece
I've copied your code in to my project directly and it gives the same error within VSCode. The same applies with both the code below from @Edwin and chamilton. I'm running TypeScript 2.7.2 installed as a local package if that makes a difference.Pasadis
@Pasadis This is weird. I upgraded to the same tsc version (though it shouldn't matter) and everything works fine for me. Create a new question with your code and I'll take a look at it.Latoyialatreece
@NitzanTomer Posted here.Pasadis
Let us continue this discussion in chat.Pasadis
If I use React.FormEvent or React.SyntheticEvent, I always get a type error that e.key is not allowed.Icaria
@Icaria I think that e.key is only part of keyboard events.Latoyialatreece
If you'd like to understand why target doesn't work see https://mcmap.net/q/101627/-property-39-value-39-does-not-exist-on-39-eventtarget-39-in-typescriptTaxidermy
Y'all, you need to use React.ChangeEvent<HTMLInputElement> this will fix your name, value undefined errorPotamic
C
73

The problem is not with the Event type, but that the EventTarget interface in typescript only has 3 methods:

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;
}

So it is correct that name and value don't exist on EventTarget. What you need to do is to cast the target to the specific element type with the properties you need. In this case it will be HTMLInputElement.

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

Also for events instead of React.SyntheticEvent, you can also type them as following: Event, MouseEvent, KeyboardEvent...etc, depends on the use case of the handler.

The best way to see all these type definitions is to checkout the .d.ts files from both typescript & react.

Also check out the following link for more explanations: Why is Event.target not Element in Typescript?

Cryptic answered 7/2, 2017 at 7:29 Comment(1)
ps. looks like my type definition file is a bit outdated as it doesn't show the generic SyntheticEvent<T> interface. The answer from Nitzan Tomer is better.Cryptic
D
53

I think the simplest way is that:

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();
}
Devitt answered 10/1, 2019 at 15:48 Comment(0)
H
52

To combine both Nitzan's and Edwin's answers, I found that something like this works for me:

update = (e: React.FormEvent<EventTarget>): void => {
    let target = e.target as HTMLInputElement;
    this.props.login[target.name] = target.value;
}
Heed answered 9/1, 2018 at 21:32 Comment(2)
this.props.login[target.name] = target.value; ??? are you changing the props :oTeratoid
Just copying the line from the original question so that the answer makes sense to the OP.Heed
E
43

for update: event: React.ChangeEvent for submit: event: React.FormEvent for click: event: React.MouseEvent

Esthete answered 5/11, 2020 at 3:23 Comment(0)
L
13

I have the following in a types.ts file for html input, select, and textarea:

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

Then import them:

import { InputChangeEventHandler } from '../types'

Then use them:


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`
}

Then apply the functions on your markup (replacing ... with other necessary props):

<input onChange={updateName} ... />
<textarea onChange={updateName} ... />
<select onChange={updateSize} ... >
  // ...
</select>
Linkage answered 5/10, 2020 at 21:5 Comment(1)
I guess it depends what you want to do (it's confusing), but for me I had to change it to export type InputChangeEventHandler = React.ChangeEvent<HTMLInputElement>; to be able to use event.target.value. Using ChangeEventHandler doesn't allow to use event.target as it's undefined.Nordic
N
12

you can do like this in 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
}
Nonoccurrence answered 29/11, 2019 at 13:20 Comment(1)
What is the difference between your example and handleEvent: React.ReactEventHandler<HTMLInputElement> = (e) => { ... }Poodle
S
10

This is the recommended way as to the typescript compiler on 2022:

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

No need to add void as return value as this is done automatically by the compiler.

Silverstein answered 24/6, 2022 at 21:47 Comment(0)
A
3

For those who are looking for a solution to get an event and store something, in my case a HTML 5 element, on a useState here's my solution:

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

const handleMenu = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) : void => {
    setAnchorElement(event.currentTarget);
};
Anility answered 19/8, 2020 at 23:41 Comment(0)
M
0

The following have the same type:

let event1: { target: { value: any } };
let event2: { target: HTMLInputElement } }; 
Marcionism answered 11/3, 2022 at 11:54 Comment(0)
A
-1

Or you could just use

const handles = (e: React.UIEvent<'type of element', UIEvent>) => {
//do something
}
Amphithecium answered 20/9, 2022 at 9:36 Comment(1)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Polynesia

© 2022 - 2024 — McMap. All rights reserved.