Write Controlled and Uncontrolled Components for Forms with React and TypeScript

Shawn Wang
InstructorShawn Wang

Share this video with your friends

Send Tweet
Published 3 years ago
Updated a year ago

There are many ways to type event handlers with TypeScript, and we use the case study of controlled components to explore the 3 major ways to do this. Then we refactor to use the createRef API to write uncontrolled components, and explore the typing strategies for that as well.

Instructor: [00:01] Here, I have a classic React form that's made with controlled components. I've lifted this exact example from the React docs. The only things I've done to it are to type the events with an any type, which matches anything. This works if I type in "Egghead" in the form and I submit. It's going to alert back to me the same value.

[00:27] However, it's very easy to make mistakes. For example, if I mistype here, there's no errors that's going to be appearing. More crucially, if I mistype any values in my set state, no errors will happen as well, but I will be unable to type in my form.

[00:50] As we know, one way to avoid this is to specify the generics of the props and state. Here, I'm going to specify just the state since we don't have props. That immediately shows us the typos in our app.

[01:11] However, leaving the event type as any allows mistypings like this, which causes issues in actual runtime code. Here, we have a "undefined is not a function" type error.

[01:25] In order to type the event, we can often look to the event handler on the native DOM element. For example, the onChange event over here tells you that it expects a type of event React.ChangeEvent<HTMLInputElement>.

[01:43] We can basically just copy that and paste that into our change handler and do the same thing for a submit function on the form. Here, it expects a React.FormEvent with an HTMLFormElement.

[02:03] That immediately raises the error, the type error over here on the invalid property. We can delete that. Notice that we also get autocomplete, which further prevents typos. Now we have back our functioning form.

[02:26] Notice as well that this method of typing relies on type inference to infer the return method of the change handler. That may not be desirable in some situations. The other way to type it is to type on the left-hand side of the assignment.

[02:42] Here, I have removed the type for event. If I just type what handle change is supposed to be, React.ChangeEventHandler with the type of HTMLInputElement, then the return type is also assigned. Event is now known to be of type React.ChangeEvent<HTMLInputElement>.

[03:13] There are two valid ways to do things based on how you prefer to infer the type of your functions. Of course, if performance is not a concern, as it is in most situations like these, you can actually inline the entire function and rely on type inference within the actual event handler inside the JSX.

[03:38] Over here, it's known that onChange expects a function of this signature. Therefore, the function that's immediately applied here is inferred to have that signature.

[03:51] Finally, let's look at how to write this with uncontrolled components. We're going to need a ref, which we create with React.createRef. We're going to pass that ref into our input while getting rid of the change event.

[04:09] As you can see, we immediately run into this rather scary-looking error message, but really all it's saying is that it's expecting one of these types of valid inputs to the ref property. In reality, React.createRef could be filled with anything. They just want to be sure.

[04:29] Here, we know that we're going to just use this ref for referring to the input DOM element. We're just going to copy that exact thing from the suggestion. We're going to put it on the left-hand side and pull the ref object from the React namespace. That will resolve our type issues. Now we have a working uncontrolled component.