The default behavior of an HTML form is to submit a GET request with the values of all its child inputs as URL query parameters. In React, we'd rather handle that within our components and avoid the need for server-side form handling code. In this lesson, we'll wire up a submit handler for a form and manage the submission and rendering updates in React.
Andy Van Slaars: [0:00] We have this form wired up so that React can maintain state for the input values, but if we hit cancel we would expect the form to clear out. That doesn't happen. If I hit save, notice that our page refreshes because HTML's default form behavior is to submit through a get request.
[0:17] We haven't actually set this up to handle the submission. Let's go back to cardForm component and set up the code we need to handle our submission and our form reset.
[0:26] Let's start with our form reset. I'm going to come down here. I'm going to create a new function inside my component. I'm going to call it clearForm. clearForm is going to call setTerm and reset the term's value to an empty string. It's going to do the same with setDef. We'll just set the definition and term pieces of state both back to an empty string.
[0:49] Then I'm going to scroll down to where my form tag is defined. I'm going to add an onReset prop for this. I'm going to point onReset to my clearForm function. We'll save this. Let's go back to the browser. When this reloads, I can add some values here. When I hit cancel, both of those should be reset.
[1:12] Now let's work on the code to handle our form submission. Let's scroll back up to the top here. I'm going to define a new function. I'm going to call this handleSubmit. handleSubmit is going to get a form submission event object. The first thing we want to do to prevent that default get behavior is call event.preventDefault.
[1:37] Now we need to do a couple of things. We're going to call an API to do our save. We still have to define that API method. Then we went to send our data back up to notify the parent component that there's a new item to be rendered in its list because app.js holds the state that has our cards array. Then we need to wire this up to our form.
[2:02] Let's start with the API call. I'm going to call saveCard, which doesn't exist yet. We'll define it in a moment. saveCard is going to accept an object with a term and a definition. Because of our state values, term here matches the value for term. I can use shorthand notation for that.
[2:23] The definition key is going to match a value that doesn't match its name. I'm going to copy this. This object is going to be definition:def. We could refactor and make these names match and use shorthand for both. For now, we'll just use what we have.
[2:40] This function hasn't been defined yet. We'll define it in a moment. It's going to take this object. It's going to make the call to the server. Then it's going to return a promise. That promise is going to resolve with the card. That card is going to be the term and definition that we passed in along with the ID that's generated on the server.
[2:59] Once the card's been successfully saved, we want to do two things. We're going to clear the form. We're just going to do that by calling our existing clearForm function. Then we want to notify the parent. We're going to do that with a prop.
[3:12] We're going to call props.onSave. onSave still isn't being passed in here. We're going to define this in a moment. We're going to call props.onSave with that card. That's going to tell app.js, "Here's a new card. Update your local state with the cards array and rerender the UI."
[3:31] Before we call this prop, we just want to make sure that it exists. I'm going to check props.onSave. We want to make sure that its type is a function. If that checks out, then we'll execute the function.
[3:49] Let's define saveCard. I'm going to show my files. In services, I'm going to open up this card service module. I'm going to export a new function called saveCard. That's going to take a card object. I'll just paste in the function body here.
[4:06] This is going to make an API call to our card endpoint with the post method, the appropriate headers for the application JSON. It's going to stringify that card object and pass it in as the body. The server will save that, generate a new ID in the process. We'll get a promise back that resolves to that new card object. I'll save this.
[4:26] If we go back to cardForm, we just need to import saveCard. Up at the top, I'll do an import saveCard from services card service. Now we need to define our onSave prop. That's going to come from app.js. I'll open app.js.
[4:48] At the top of this function, I'm going to add a new function I'll call handleAdd. That's going to receive a card as an object. This is going to update our card's state. We'll call setCards, which will get the existing cards. Then we need to return the new array that adds our card to that array.
[5:08] For this, I'll just create a new array and spread existing into it and append card to the end. Now I want to take handleAdd. Down in my render, I'm going to pass that in as the onSave prop to cardForm. I'll save app.js.
[5:28] Then we'll come back to cardForm. The last thing we need to do wire this up to our form. I want this handleSubmit to run. We'll come down to our render. On our form tag, we'll add an onSubmit. We'll paste that in. We'll save it. We'll go back to the browser.
[5:49] I'll open up DevTools. I'll open up the network tab. I'll fill in a term. We'll add some test values there. We'll hit save. We'll see that this gets added to our grid. Our form gets cleared out. Our API call is made, passing in our form and responding with that new card object that includes the server-generated ID.
[6:14] Let's go back to our code, do a little bit of cleanup. In cardForm, I'm going to scroll up to our handleSubmit. I'm just going to remove these comments. All of these tasks are done. For saveCard, I'm using the shorthand syntax for term, but I can't do it with definition because of the name of the state value.
[6:36] What I'm going to do is I'm going to refactor this. Since I'm in VS Code, I'm going to hit F2 on the const def. This is going to allow me to rename a symbol. This will carry through everywhere that I reference this variable in this component.
[6:50] I'm going to make this definition. You'll see that it updated down here. It also updated down in our render. Here, we don't need it anymore, so I'll take it out. Then we can just verify that this works by scrolling down. We'll see that the value for the text area no longer references def from the state but the full word "definition."
[7:12] With that, I'll save cardForm. I'll go back to the browser. I'll just verify that my refactor hasn't broken anything. We'll throw another sample term in here. We'll hit save. We'll see that our UI is updated. Our API call was made. Everything's still working as expected.