Use Volatile State and Lifecycle Methods to Manage Private State

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 5 years ago

MST has a pretty unique feature: It allows you to capture private state on models, and manage this state by using lifecycle hooks. For example by setting up a WebSocket connection and disposing of the connection automatically as soon as the instance gets removed from the store. In this lesson, we will leverage cancellable fetches to abort in-flight requests when appropriate

In this lesson you will learn:

  • Aborting window.fetch requests :-).
  • Storing private, volatile, internal state in the function closure
  • A second life-cycle hook: beforeDestroy

Instructor: [00:00] Our application can show data from the server. However, there's no reload button yet, so it cannot get updated from the server. To do that, we're going to just add another small function. We'll call it reload for now.

[00:15] To keep things simple, we just call again. Of course, we need a tiny button that triggers it. This looks pretty OK. We can reload the data, and if we take a look at our network requests, we see that it nicely fetches the data.

[00:34] Actually, there's a problem. Because I started the server with a hard-coded delay, every request has a delay of one second. If I click the button a lot, there are a lot of requests going on. In fact, it would be nice if we would cancel the first two.

[00:53] There are libraries which do support it. However, the fetch API doesn't do that, except for Firefox and Microsoft Edge. They both support cancelable fetches. To do that, we have to abort the current request. The way to do that is to create an abort controller.

[01:16] We instantiate it, and when we create a fetch, we pass on a signal. That one is coming from the controller, so this binds our controller to our fetch API. On the controller, we can call abort, and this will cause the fetch API to cancel.

[01:34] If the request is aborted, this will reject the promise, so we do want to catch that. Because we're using generators as flows, we can use try/catch, just like we can on using async/await. We can now log that the request is aborted. Otherwise, we log success.

[01:56] While we are at it, we can leverage the beforeDestroy hook that if, for whatever reason, this group is ever unloaded from memory, we make sure that we can abort any pending request.

[02:08] Our problem is that we don't have access to this controller yet, so where do we store it? We could potentially put it in a model, but that's not really nice, because it's not really a state which is exposed to the outside world. This is just some internal state of the model.

[02:25] Also, a controller is a native dumb thing. It's not something that is serializable through a JSON or something, so it cannot be in a model. Models can only be serializable. What we can do, though, is to store this controller in the closure of the actions.

[02:45] Instead of returning an object literal directly, I'm going to rewrite this action initializer a little bit and have it return all the actions explicitly. Now we can lift the controller to the function scope. In our logged action, we assign the controller which is declared in our function scope, and only our actions can access this controller.

[03:12] Further, it's not accessible for the outside world.

[03:16] This is what we call a volatile state, a state that lives as long as the instance of a group lives. Because this is just a function that's being executed, every instance of every group in our application will have its own local volatile state. There we go.

[03:33] Now if you open a network button, we see all those user requests nicely passing by if I click them slowly. However, if I fire a bunch of requests, I see that only the last one completes, and the other two never complete. Why? Because they're aborted. If I check the logs, this is what we expect. We see some succeeding, some being aborted because a new request is fired.

[03:58] If we look at Chrome and refresh reloads, we see all the requests finishing one by one. They cannot be aborted. Chrome doesn't support it. In Firefox we can, and we use the life cycle of the group instance to keep track of the internal state of the application so that we can abort the current outgoing request in the reload function.

[04:21] Now, you can imagine that this is very generically applicable. In the volatile state, you can store states you need to track of for the internal working of this component. This might be Web circuits connection or whatever, and then you can nicely deal with them in the lifecycle hooks, like afterCreate and beforeDestroy.

~ 5 years ago

Chrome 66 now supports AbortController:

Only IE is left out, so it's pretty safe to use.