How to Manage User Events in React With TypeScript
This tutorial will show how to manage users’ events in React with TypeScript by passing an onClick
function from component to component on a user’s actions.
Manage User Events in React With TypeScript
We will be using create-react-app
to quickly start and run a new React project.
npx create-react-app my-app --template typescript
cd my-app
npm run start
After installing the necessary packages and starting the development server, we will go to src/App.tsx
, delete all the boilerplate code and leave an empty component.
import React from "react";
function Message() {
return <div></div>;
}
export default Message;
Now we will add a button to the div
that the user can click, and we will respond by passing a function with an alert inside the onClick
prop.
function Message() {
return (
<div>
<button
onClick={() => {
alert("I was clicked!");
}}>
Click Me!
</button>
</div>
);
}
Nothing changes from Vanilla React to TypeScript, but things are different once we want the onClick
function to be passed as a prop of the Message
component. To show this, we will create another component called Game
that will have Message
as its child.
function Game() {
return (
<div>
<Message></Message>
</div>
);
}
export default Game;
And we will make Message
receive its onClick
function and text
as props coming from Game
.
function Message({onClick, text}) {
return (
<div>
<button onClick={onClick}>{text}</button>
</div>
);
}
function Game() {
return (
<div>
<Message
onClick={() => {
alert("I was clicked!");
}}
text="Click me!"></Message>
</div>
);
}
However, we will get the following compilation errors if we run this code.
Binding element 'onClick' implicitly has an 'any' type.
Binding element 'text' implicitly has an 'any' type.
In Vanilla JavaScript, this doesn’t cause an error, but TypeScript throws one since Message
’s onClick
and text
props implicitly have an any
type, i.e., we didn’t declare which type those props should store. We must create an interface specifying which type the Message
’s props should have to solve this.
interface MessageProps {
text: string;
onClick: {};
}
The value that the text
prop should have is easy to declare since it’s just a string. But the value for onClick
is more difficult.
onClick
is more than a regular function since it has an event
property, and it is a predetermined property of the button
element. So to define onClick
, we need a predefined interface that comes with React, which in this case is called ButtonHTMLAttributes
and holds all the property types from the button
element.
To use it, we have to extend the MessageProps
interface to store the ButtonHTMLAttributes
types.
interface MessageProps extends ButtonHTMLAttributes {
text: string;
}
However, this isn’t enough, and running the code like this will throw an error since the ButtonHTMLAttributes
interface is a Generic Type. You can think of Generic Types as interfaces with variables, and to use them, we wrap them around <>
after declaring the interface.
In this case, the ButtonHTMLAttributes
interface needs a variable to know which HTML element we are using, and it will be the global HTMLButtonElement.
interface MessageProps extends ButtonHTMLAttributes<HTMLButtonElement> {
text: string;
}
MessageProps
doesn’t only hold the types for the text
and onClick
props but the types for all props of a button
element. You could add any prop from a button
to Message
.
In case you want to only extend the onClick
prop, you don’t extend the interface, create a new onClick
type and assign the ButtonHTMLAttributes
’s onClick
property using Indexed Access Types.
interface MessageProps {
text: string;
onClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"];
}
To finish, we must declare that the Message
component will use MessageProps
for its props in the following way.
function Message({onClick, text}: MessageProps) {
return (
<div>
<button onClick={onClick}>{text}</button>
</div>
);
}
And if we want, we can annotate the return type to be a JSX.Element
so TypeScript will throw an error if we accidentally return some other type.
function Message({onClick, text}: MessageProps): JSX.Element {
return (
<div>
<button onClick={onClick}>{text}</button>
</div>
);
}
And this would be the final result.
import React from "react";
import {ButtonHTMLAttributes} from "react";
interface MessageProps {
text: string;
onClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"];
}
function Message({onClick, text}: MessageProps): JSX.Element {
return (
<div>
<button onClick={onClick}>{text}</button>
</div>
);
}
function Game() {
return (
<div>
<Message
onClick={() => {
alert("I was clicked!");
}}
text="Click me!"></Message>
</div>
);
}
export default Game;
Juan Diego Rodríguez (also known as Monknow) is a front-end developer from Venezuela who loves to stay updated with the latest web development trends, making beautiful websites with modern technologies. But also enjoys old-school development and likes building layouts with vanilla HTML and CSS to relax.
LinkedIn