React Event Handlers with TypeScript
In this post, we’ll cover how to implement React event handlers that have strongly-typed parameters with TypeScript.
React event types
The React event system is a wrapper around the browser’s native event system. TypeScript types for this event system are available in the @types/react
npm package. These types can be used to strongly-type event parameters. Some of the common ones are:
ChangeEvent<T>
KeyboardEvent<T>
MouseEvent<T>
FormEvent<T>
These types are all derived from SyntheticEvent<T>
.
All the event types are generic and take in the type for the element that raised the event. For example, MouseEvent<HTMLButtonElement>
could be used for the parameter type for a button click handler.
Inline event handlers
If the event handler is implemented inline in the JSX element, it is automatically strongly-typed. Consider the example below:
<input type="text" value={criteria} onChange={(e) => setCriteria(e.currentTarget.value) }/>
TypeScript is able to infer the type of e
to be ChangeEvent<HTMLInputElement>
. So, we get nice autocompletion when we reference e
:
Named event handlers
The typing is a little more tricky when a named function is used for the event handler. Consider the example below:
function Searchbox() { const [criteria, setCriteria] = React.useState( "" ); const handleChange = (e) => { setCriteria(e.currentTarget.value); }; return ( <input type="text" value={criteria} onChange={handleChange} /> );}
TypeScript infers the type of the e
parameter in handleChange
to be any
. So, no type checking will occur when e
is referenced in the handler implementation.
Not good!
We can use a type annotation to define the type of e
explicitly. What type should e
be set to? Well, a neat tip is to hover over the event handler prop to find out:
So, the type is ChangeEvent<HTMLInputElement>
. So, a strongly-typed version of the handleChange
event handler is as follows:
const handleChange = ( e: React.ChangeEvent<HTMLInputElement>) => { setCriteria(e.currentTarget.value);};
Neat!
Cross element event handlers
What about event handlers that handle events from multiple elements? Consider the example below:
const handleChange = (fieldName: string) => (e) => { ...};
return ( ... <input type="text" placeholder="Enter your name" value={values.name} onChange={handleChange("name")} /> <textarea placeholder="Enter some notes" value={values.notes} onChange={handleChange("notes")} /> ...);
The e
parameter in the event handler is of type any
at the moment.
We can use the ChangeEvent
type for e
, but what element type do we pass into this generic type? The event handler is handling events for two different elements - HTMLInputElement
and HTMLTextAreaElement
. We can use the union type, HTMLInputElement | HTMLTextAreaElement
, for these elements. So, a strongly-typed event handler is as follows:
const handleChange = (fieldName: string) => ( e: React.ChangeEvent< HTMLInputElement | HTMLTextAreaElement >) => { ...};
We can also narrow down the type for the fieldName
if we have a type for all the field values. In this example the type of values
is:
type NameAndNotes = { name: string; notes: string;};
So, we can improve the typing of fieldName
with the keyof
keyword as follows:
const handleChange = (fieldName: keyof NameAndNotes) => ( ... ) => ...
Neat!
Wrap up
Standard event types for React are available in @types/react
. This includes generic types that can be used to strongly-type event handler parameters by passing the type for element raising the event.
TypeScript can infer inline event handler parameter types. We need to explicitly use a type annotation for the parameter in a named event handler. We can hover over the event handler prop to discover what the handler parameter type should be.
Learn React with TypeScript - 3rd Edition
NewA comprehensive guide to building modern React applications with TypeScript. Learn best practices, advanced patterns, and real-world development techniques.
View on Amazon