Sometimes you need to cancel a pending request based on an action performed by the User. In this lesson we’ll see how we can stop an in-flight ajax request by dispatching another action into the store and making use of the Rx operator ‘takeUntil’.
Instructor: [00:00] Results from this search box take a few seconds to appear on screen. That's because in the code, we've added this artificial delay just to simulate what it might be like in the real world on a slow connection. In that case, we want to allow the user to opt out of the current search by clicking a button.
[00:21] First, we'll model the data in our reducer and copy this set status and make a new case for cancel. When a cancel action comes through our store, we're going to set the status back to idle and then clear any previous messages.
[00:38] We need to create this constant and the action creator for it. We'll come in here, copy that. If we go down to fetch data, we can just copy that. Back in the reducer, we can now import it. Now we have the data model for it, we can go into our component.
[01:02] Just next to the search box, where we have this spinner, we can add another element here, which will just be a button that says, "Cancel." When clicked, it will call the cancel function. This will come from our props. We'll get that by passing it in down here. We'll import that from our action creators.
[01:35] Now when a search is taking place, we'll go into this pending state and show this button. When clicked, we call the cancel function, which will dispatch this action, put us back into an idle state, and clear any messages. Let's see what it looks like so far in the browser. Create a search. Click cancel.
[02:00] We're still seeing elements on the screen. What's happened there? If we go into our Redux store, you can see that we have dispatched the cancel action, which set the state to idle, but the fetch fulfilled from the AJAX request still occurred, which set the status back to success with some data.
[02:22] This is where we need to go back to the epic and use the click action to cancel the AJAX request. As always, in Rx, the way to solve this is through composition. We can use one stream or one observable to cancel another.
[02:37] In our case, this is the observable that is producing the data from the API. If it takes a long time, like we've got this fake delay here, we want the cancel action to prevent this from dispatching an action into the store.
[02:50] We can do that with takeUntil. It's another Rx operator that we can import. This accepts as a parameter another observable. We can use that actions stream that we have here. We can say, "of type." This time, we're going to listen to the cancel action. We'll import that constant. That's it.
[03:15] The way takeUntil works is that it accepts any observable here, regardless of what type of value it produces. All it does is internally, it will subscribe to this observable. If it ever produces an element, then it will unsubscribe to its source, in this case, whatever comes before it.
[03:38] The real beauty of this approach though is all the things we're getting for free. For example, subscribing to this observable is not something we have to do manually. This will just happen at the right time.
[03:49] Not only that, but if this does produce a value, takeUntil will ensure that we unsubscribe to this one as well. It will only take one item. Also, the thing you end up unsubscribing to, in this case, the AJAX request, that will execute its teardown logic.
[04:05] In the case of an AJAX request, that will mean canceling it, but it could equally be you listening to an event emitter or some other DOM API that has any logic to do with unsubscribing or removing an event listener or anything else.
[04:19] Let's just check it all in the browser. If we perform a search, cancel it, you can see that we get this cancel action in the store. It sets us back to an idle state. We no longer get the fetch fulfilled. That's because the AJAX request was unsubscribed to before it could complete, thanks to this takeUntil.