Request management made easy with @zup-next libraries for react: Part 3.

Tiago Peres França
6 min readMay 28, 2019

--

Welcome to the third and last part of this series of articles. If you didn’t read the previous parts yet, you can find them in the following links: Part 1, Part 2. The complete code for the demo application that we’re building in this series of articles can be found here.

Motivation

We, front-end coders, are directly responsible for the user’s experience in our applications. Each micro-interaction, tweaks and improvements we make are made so the user might have a better time when using our products. At Zup-IT, we have this concern, which was made apparent in one of our last projects where, although the application was functional and had great feedback from the client, we knew we could make it better by reducing the number of loadings through a more intelligent control of the requests we make.

It was following this line of thought that we decided to create the library which is the basis for this last part of the series. We’re going to talk about it now and better explain how it considerably improved many aspects of the application for our clients and also for us, developers.

Improving the demo application

Getting back to our demo application, we can say that it has already been implemented and works very well, but we can still make it better. If you open the Network panel, in the developer tools for Chrome, you’ll see that some resources are called multiple times when we don’t really need to be calling them again. The following image shows the state of the Network panel in the catalog screen just after placing an order for a movie.

Network panel after ordering a movie and showing the catalog (without cache)

Notice that the URL “/catalog” was called three times and the URL “/wallet” was called two times. Since the load operations happen in the componentDidMount phase of each component, every time these components are showed in the screen, the requests are triggered. We find situations like this in many applications, sometimes the page needs a resource that has already been loaded by another page, or, sometimes, the component is appearing for the second time and doesn’t need to fetch the resources it has already fetched in the first time.

We could fetch the resources in the first page they’re needed and never load them again, but this is a terrible idea. What if the user enters the page directly from the URL? Besides, when initializing each container, we’d need to know beforehand the resources that has already been loaded and the ones that has not, which would make our code a lot more complex.

Another solution would be to verify if a resource has already been loaded before loading it again. Unfortunately, in some situations, we really need to refresh the content. By doing this, we’d end up with a collection of “ifs” that could also make our code very complex in the future.

The solution we want is one capable of remembering the resources that have already been loaded and forget them at the right times, but we don’t want to add any complexity to our existing code. In our containers, we want to keep the simplicity of dispatching the action “load” of a resource every time we need it, without having to worry if it has already been fetched or not. i.e. we need an intelligent cache system that can be configured outside our containers. We named this system redux-action-cache.

Since all of our load actions are managed by redux-saga, we had the idea of intercepting the redux actions and take a decision of whether to process or not the action according to our needs.

The redux-action-cache is a middleware that allow us to cache actions from redux, i.e., every time an action is dispatched, before it can proceed, it goes through our middleware which acts like a gateway, deciding if the action will be ignored or if it’s going to proceed to the next middlewares and reducers. An action is ignored every time there’s a valid cache for it. The application starts with an empty cache, as soon as an action is dispatched, if there’s a cache rule for it, a cache is created, in a way that, if it’s dispatched again, it will be ignored until this cache is no longer valid.

It is very easy to set up the the redux-action-cache. We must first define the rules for our application, then we just need to register the middleware to redux. Attention: our middleware must be the first to be defined so it works as a gateway! It doesn’t matter if we ignore an action after it goes through redux-saga, for instance.

Getting back to our movie store application, we can extract the following cache rules:

  • Cache every action where “type” ends with “/LOAD”. These action types are defined by redux-resource. They are the value of the field “types” in the result of the function “createResource”. See here a list with all action types.
  • The load actions must not be cached if an error occurs while making the request. It is, if we dispatch a “/LOAD” after a “/LOAD_ERROR”, “/LOAD” must be processed by redux, no matter if it has been cached before. In other words, every “/LOAD_ERROR ”must invalidate its corresponding “/LOAD”.
  • The action load of the resource “wallet” must always be refetched after an order is successfully placed, i.e. an action of type “ORDER/CREATE_SUCCESS” must invalidate the cache of the action “WALLET/LOAD”.

Having set up the rules for our application’s cache, we just need to write them in the format expected by the library redux-action-cache.

In the code above, in “include”, we defined an array with all action types that must be cached. Another way of achieving the same result would be using a regex. See the example below:

In the invalidations property, we need to use regex and capture groups to define that every action following the format “(x)/LOAD-ERROR” must invalidate the action “(x)/LOAD”. Instead of using regex, we could have written every error and load action, but the code would be way longer.

When creating the redux store, we need to include our middleware:

Done! It’s that simple! Now, if we run the application again, buy a movie and return to the catalog, we’ll see that only the necessary requests will be sent.

The catalog is now called only once! Many containers use it, but, in fact, the catalog is just one, there’s no need to reload it every time a container is mounted. The resource “wallet” continues to be called twice because, when setting the cache rules, we said that the cache for it should be invalidated every time an order is created.

The economy made is not much in this simple application, but in the real world, the redux-action-cache makes a huge difference with a very small code footprint. In the beginning of our conversation, we mentioned the importance of front-end developers to care about every detail of the experience they’re providing to the user. The redux-action-cache approach allowed us to improve the communication between the user and the application, lowering the number of requests sent to the server, which brought us many advantages, like the reduction in the number of loadings in the application and reduction of the usage in the back-end server.

Here we used a very simple configuration for the cache, but the library offers many customization options like: expiration, persistence and cache based on properties other than the action’s type. For more information, please, read our documentation.

We finally got to the end! Along these three articles, we created an application with redux-resource and redux-action-cache. Through these libraries, we made it possible to simplify all the code responsible for request management. Furthermore, we created an intelligent cache with a very easy configuration. Thank you for reading! If you have any idea or suggestion, do not hesitate using GitHub to create issues or submit pull requests. Below, we leave a list with the most useful links related to the content of this series of articles.

I’d like to thank my colleagues Isac e Raphael de Souza for co-authoring the library and this article.

--

--