Today, I learned that listening to the onChange
event is different in TypeScript-based React form components. Normally, the approach I'd take would look like this;
And, that's all one would need to keep track of the state in the name
input field of this form component. But it is a bit different with TypeScript, because of its "type safety" perk.
the TypeScript approach
In TypeScript, here's what the component would look like. Pretty much the same thing, but the event handler function is different.
Here's how the snippet — especially the handler — above works;
The handler accepts an argument, (event: React.ChangeEvent<HTMLInputElement>)
which is its type signature, indicating that it accepts an event of type React.ChangeEvent<HTMLInputElement>
. Accepting the HTMLInputElement
means that we're expecting the ChangeEvent
interface to dispatch an event whenever there's a change in the value of an HTML input element.
TypeScript will enforce that the event
argument passed to handleNameChange
is of the correct type and that we can access the value
property of the input element without any runtime errors.
The event
parameter is an instance of the ChangeEvent
method from React. It contains the information about the change event that triggered the callback function — the handler function, in this case.
event.target
refers to the element that triggered the event, in this case, an <input>
element.
event.target.value
is the new value of the input element.
setName(event.target.value)
from the useState hook updates the state of the name
variable to the new value.
Multiple input fields
You might also want to manage multiple input fields and not have to keep doing React.useState()
for every input state. Luckily, there's a way we can co-locate multiple state values in a single useState
hook.
Here's what a typical useState()
value would look like, if we were to accept multiple values.
To make it type-safe, we can even go ahead to create a component interface for the values our form component should expect.
This alone doesn't doesn't manage the input state. We still need a way to track the state of individual input fields. To do this, we'd modify the handleNameChange
function. But, this time around to be responsible for all the fields.
On line two, an object destructuring assignment is used to obtain the name
and value
properties from the synthetic event, which in turn is used in the setInitialValues
setStateAction, with the previous input values spread appropriately.
Textarea fields.
If you try to use the onInputChange
function as is on a textarea
element, TS will scream at you. So, to avoid this, you need to pass its corresponding type like so:
my thoughts?
I feel like this is just unnecessarily too "much", Lol, if we're, to be honest. But, the fact that we can define event handlers this way, and ensure type safety in our code is something i can refer to as "cool"
In JavaScript, one could accidentally pass the wrong type of event object to an event handler and not catch the error until runtime.
But, with TypeScript, as they say:
you get compile-time checks that help prevent these kinds of errors.
As always, I'll keep documenting my process with TypeScript here.
Update: No. It isn't unnecessarily too much. If you can, please use TS, It'll save you a lot of time