Make Dynamic Forms with React

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 7 years ago
Updated 5 years ago

Static forms don't give the user a lot of information about how to submit the form. We can use Reacts onChange prop on an input to dynamically update the form; disabling and enabling the submit button on a condition. This allows for custom validation as the user makes changes to the input.

Instructor: [00:00] Here, we have a name form. If I say hi in the name form, I'm going to get an error that the value must be at least three characters, but it's only two. Then I say hey, and then it says it must include S. OK, fine, I'll do heys, and now I have success.

[00:14] This kind of experience is not really super awesome, because I have to keep on trying and hitting submit. What would be much better is if the submit button were disabled or something, not even rendered, and I get an error message as I'm typing.

[00:27] That's the kind of experience we're going to build. The way that this works right now is we have this form that we're rendering, and on submit, we go ahead and prevent the default behavior for the form. We get the value from the username element, and then we get the error from our getErrorMessage prop.

[00:45] If there is an error, then we alert with the error, otherwise we alert with success. Our getErrorMessage prop here is a function that accepts a value and validates it. If we want to validate this thing in real time, then we're going to need to keep some state around that tells us whether or not this is valid.

[01:00] I'm going to go ahead and add a state property here that has an error state. We'll just start that as null. Then on our input, we'll add an onChange. Here, we'll say this.handleChange. Then we'll create a public class field here called handleChange, and this handleChange is going to accept our event.

[01:22] We can get the target. We'll get the value out of the event.target, and then we'll call this.setState with error being this.props.getErrorMessage with the value. Then inside of our render method, we can get the error out of the state.

[01:43] Here, we can say the button is disabled if the error exists. We'll cast that to a Boolean. I say H-I, and I see that it's disabled. Then an S, and it's enabled. If I refresh, this is actually going to initialize as enabled, because we haven't actually done the error message check yet.

[02:01] Let's go ahead and do that when we mount. We'll say componentDidMount, and we basically want to do this same behavior here. We'll just put that right there. Then we don't actually have a value here yet, so we'll just get rid of that, and replace it with an empty string.

[02:14] Now it's initialized with a disabled button, and then we can type his, and it gets enabled. Perfect. Now, let's go ahead and render an error message if the error does exist. We'll say if there's an error, then we'll render a div with the error, otherwise we'll render nothing. Just for fun, we'll add a style with the color of red.

[02:42] Now, we have that error message being shown up right now. You can say hiy, and we get that new error message. Then the error message is gone. In review, to make this work, we didn't actually have to control the value of the input. We just needed to make sure we knew when that input was changing, and we handle that change.

[03:00] When that happens, we say, "Hey, get me the error message," and that sets our error state, which will cause a rerender. If that error does exist, then we're going to render the error message, and we'll also disable the button.

[03:13] We also initialize our state in componentDidMount. Instead of setting state in componentDidMount, we could also just move this directly into the initialization of our state, because at the time that this runs, this.props will already exist, and we'll be able to get that error message.

[03:28] Let's go ahead and do that instead, and everything will work exactly as it had before. In addition, we've also avoided an unnecessary rerender, because we're not actually setting state, we're just initializing the state properly.