Adding Pending State to Form Submissions with Remix
00:00 When the user submits a form, they're actually performing a navigation, just like if they were clicking on these different links. Submitting a form, like clicking the delete button, takes them from one place to another. Now, that's not always the case. Sometimes you're going to favorite a post or something like that, and that's not going to result in navigating from one page to another.
00:19 But in the context of what we're doing right now, we are navigating. There's only one navigation possible at a time. So I can navigate to this page, and I can click on edit, I can submit this form, and that navigates me over here, but I can't also at the same time be navigating elsewhere.
00:35 And so, there's a concept of navigations as we're going around our application. So if I go to our root, and I just add a console.log of useNavigation, and here, let's give that
00:49 a variable name to make that a little more clear, navigation is useNavigation. So now, if I look at the logs right here, as I navigate around, whether I'm clicking on links or I'm submitting forms, I'm getting navigations for each one of these things.
01:07 So this is state loading, we're loading data, and then we get there, now we're idle, and then we clicked on another link, so we're loading more data. And now we submitted a form, so now we're in a submitting state. And when we have a submission, we actually also have this form data object that we can get properties off of, that should be familiar to you.
01:24 Our actions have form data, and we have access to that form data in the client. We have all of the information that we need to know about the navigation that's currently going on. So one of the things that the navigation will have is the form action, so what URL is being
01:39 posted to in the case of this, as well as the form encoding type and the form method. So we can use that to determine whether or not the navigation that's currently taking place is submitting this form. And if that is what's happening, then we can show a nice spinner on the submit button.
01:58 So that's the idea, that's what we're going to try and accomplish here with our form submission. So let's clean this up, we'll go to our editor, and come down here, we'll stick this right there. We've got our navigation, and let's bring that in from Remix Run React.
02:14 Now we just need to determine, is the form action correct, and is the form method correct, and is it currently in a non-idle state? So if it's submitting or it's loading, we want to show a spinner. Submitting is when the user is sending data, loading is when Remix is going to get data from the loaders. So in either of those cases, I think a spinner is appropriate.
02:33 So as long as it's not idle, we'll show the spinner. So here we go. Let's say, isPending, if the navigation.formAction is equal to this URL right here. We'll copy that. Oh, but it's got a couple dynamic properties in there, so we'd have to construct it.
02:51 Would it be nice if, like, we don't have to specify a form action here, it would be kind of nice if we didn't have to specify a form action here either. And so, we're going to do that. We're going to get the form action from useFormAction. And Remix will use the same logic around how it determines what the form action for
03:11 this form is to get the form action here. So we can more easily make this assertion. So now we know, isPending is assigned to the fact that the navigation that's currently happening is at this URL.
03:25 But we also need to make sure that the navigation that's happening is a navigation.formMethod is a post. So we only care about post requests.
03:41 We're not caring about whether we're reloading this or navigating to this page or anything like that. We only care about the post because we're trying to show it on the Submit button. And then finally, we want to make sure that it's not idle. So we say navigation, not pathName, but state is not equal to, oh, idle is not an option
04:00 here because it can't be when there is a form action and a form method. So then we got a couple options here. We could just leave this off and this would be sufficient. But I kind of like having that form state is not equal to idle. So we'll add that earlier in our chain here.
04:19 And now, idle will be one of the options. So either way, this is kind of functionally equivalent. This line is kind of unnecessary because we have these other two, but I just like the way it looks. So, you know, you got to have some things in life that you enjoy. And apparently that's it for me.
04:37 So we've got our isPending state here now. And so we're going to switch this Submit button to be disabled when we are in a pending state. Additionally, we're going to switch it from a regular button to a Submit button or a status
04:55 button and bring that in. And then we can specify the status with, if it is pending, we'll say pending, otherwise idle. And that should get us rolling.
05:12 So now we can look at our network. We can throttle this down to a slow 3G network connection. And when I make this change and click on Submit, we get our spinner and we get the disabled button. So the user can't just click on it a bunch of times, but we can give them a really nice
05:29 bit of feedback so that they know, oh, you are paying attention. And then of course, we can apply this to all the different forms and buttons and everywhere so that we can give the user a nice experience. Now this also, this same strategy can be applied to network clicks or different pages.
05:46 You can, there are a bunch of other really cool things that we can do to give a nice loading experience in all sorts of places in our app. And that may seem like a lot of work and it kind of is, but there are abstractions you can build around this.
06:01 And like we're building apps that are better than multi-page apps, the full page refreshes that we have to do. And so it's going to take a little bit of work. But luckily we do have some abstractions that we can build around this to make it quite a bit easier for this type of an experience. But that's it.
06:20 You get the navigation, you determine whether that navigation is because of the thing that you think it is. So the submitting of this form, and then you use that to determine whether or not you should show your pending state and then you show it. And that is how you add pending state with Remix.