Passing Functions in React with TypeScript
Feb 6, 2019 • 6 Minute Read
Introduction
If you’re new to TypeScript or React, it might not be obvious how to pass a function to a button or another component to change the state of your current component. Due to JavaScript’s “this” binding, it can become very confusing. This guide will show you how TypeScript can make this easy. In doing so, we will build a simple app to change the color and text of a box with a button. This would be useful for a teacher who wants to know if their students are understanding the material.
Defining TypeScript Functions
Here is an example of how to create a Toggle Button for the user to click.
import * as React from 'react';
interface ToggleProps {
ClickHandler: (event: React.MouseEvent<HTMLButtonElement>) => void
}
export function Toggle(Props: ToggleProps) {
return <button onClick={Props.ClickHandler}>Toggle</button>
}
In this component, we create an interface that defines what kind of props our Toggle component should receive. We are expecting only one prop called ClickHandler. It takes a function and returns void. As a sample reference, the ClickHandler prop function is currently set up to receive an event parameter of React.MouseEvent type that is applied to an HTMLButtonElement. Any time you click on an element in JavaScript with your mouse, it receives this event property by default. We won’t be needing it in our example. If you did need information about the mouse click event, this is how you would let TypeScript know about the type of event and the type of element that was clicked.
Adding a Component to Use the Toggle Button
Next, we need to import our Toggle button into the component we plan to use it with. We will refer to this as our StatusCard component.
import * as React from 'react';
import { Toggle } from '../Toggle';
interface StatusCardProps {
DefaultStatus: StatusTypes
}
interface StatusCardState {
Status: StatusTypes
}
type StatusTypes = "Good" | "Slow Down" | "Stuck";
export default class StatusCard extends React.Component<StatusCardProps, StatusCardState> {
static defaultProps = {
DefaultStatus: "Good"
}
state = {
Status: this.props.DefaultStatus
}
toggleClickHandler = () => {
this.setState((prevState) => {
if (prevState.Status === "Good") {
return { Status: "Stuck" };
}
if (prevState.Status === "Slow Down") {
return { Status: "Good" };
}
return { Status: "Slow Down" };
})
}
getColor() {
if (this.state.Status === "Good") {
return "#0F0";
}
if (this.state.Status === "Slow Down") {
return "#f4cf53";
}
return "#F00";
}
render() {
return (
<>
<div
style={{
background: this.getColor(),
color: "#000",
height: "200px",
width: "200px",
}}>
{this.state.Status}
</div>
<Toggle ClickHandler={this.toggleClickHandler} />
</>
)
}
}
Let’s break this down from the top.
First, we declare our component props and state interfaces, so TypeScript knows what type of props this component should receive and what type of state it keeps track of.
Inside our React component, you will notice a special static defaultProps property that allows us to tell TypeScript what props are not required and have a default set. You can read more in TypeScript’s documentation about setting default props for React.
We then set our default component state.
The next method is our toggleClickHandler. This method contains the logic we want to send to our ToggleButton component when it gets clicked. It will change the state of this component. The important part to notice is the use of the arrow method to create the function. This automatically binds JavaScript’s “this” to the StatusCard component. Without it, when we call this.setState JavaScript would look in for the state of the actual button that was clicked instead.
You can do this without using the arrow function but it adds more syntax. You have two alternate options. Both require using JavaScript's bind method.
You could add a this.toggleClickHandler = this.toggleClickHandler.bind(this); into your class constructor.
You could also pass this.toggleClickHandler.bind(this) into the Toggle Button Component ClickHandler prop.
To finish our component, there is a simple getColor method to display a different color and text depending on the state of our app. Last is our render method. The render method displays our component and the Toggle button component. Here we can pass our toggleClickHandler as a property to our Toggle component.
Conclusion
This guide has explained how to useto use a Toggle Button type component with a variety of other components. Each component can tell the Toggle Button what to do when clicked.
It should be noted that, in the above examples, you could have used a plain button component instead of creating a special Toggle component that wraps around the button. The Toggle component shows an extra layer of complexity when it comes to passing our toggleClickHandler method down multiple components. In this case, our toggleClickHandler gets passed to the Toggle component. The Toggle component then passes it to a plain button component. The whole time, our toggleClickHandler will remain connected to our StatusCard component when changing the state.