• Pratik Bhattacharya

State Management for Front End Applications (Part I) - What and Why


Recently we dealt with State and State Management in our Frontend app. The app is a UI level integration platform which hosts and integrates multiple smaller apps and provides a unified and fluid experience to a user-base of more than 30K. About 15 teams are developing their app and interacting with the shell application. So state for us at the platform level is a complex concoction of AJAX calls, local storage, session storage, in-mem variables, global objects, index-DB etc. We realized that without investing in a concrete state management methodology the complexity would only aggravate. We went deep into understanding how a state should be managed and it can be done at an enterprising level especially when multiple isolated apps are involved. With all my learning and analysis I finally decided to pen down my thoughts in this series of blogs termed as "State Management for Frontend applications".



View Unpredictability


Developers who have worked with big frontend applications realize that maintenance of such applications, if not written correctly, can be a painful task. We have all faced situations where we have struggled to find the code in a sea of JavaScript that gets triggered when a button is clicked on the screen. Especially when multiple frameworks like Angular, React or Vue is involved in this problem accentuates. The reason for this is, there are multiple ways to bind to the click action to an HTML element and also this binding can happen at multiple places. We can use a standard onClick method to bind the click to a method. If we are in a form then we can use an onSubmit method on the form to do the same. The same can be achieved by pure DOM manipulations like document.getElementByid('button').addEventListerner('click', …). If you are using frameworks like jQuery (believe me it's not as obsolete as we may like to think) then the syntax can be different. Another example of this lies in binding data to the View. Like, let's say we want to show an error banner with a text on the screen. If we are using a UI framework like Angular we might resort to using two-way data binding and custom attributes like *ngIf to achieve the same. In React the mechanism is different, and the same can also be achieved by DOM manipulation in JS. In case the application that you are maintaining has multiple JavaScript files then the problem is accentuated, as there is no way to determine which JS file might contain the code or method that is making these DOM manipulations.

One way to deal with such an issue would be to maintain a strict coding pattern for your application. However, with multiple developers working on a project plus new developer joined, old developers, moving on, with each having their style of coding, have a strict pattern is harder than one thinks. New developers who are unsure and unaware of such patterns may make code changes or add features in ways that they are comfortable. Or there can be situations where enthusiastic developers introduce the latest frameworks into the codebase. But since the codebase is huge one may decide to incrementally move the new features or change requests in the new framework. This would now result in your codebase doing the same type of operation (like showing a text on the screen or handling a button click) in different ways.

This conundrum is what we refer to as an unpredictable View.


Componentization


A novel way to tackle this problem would be to break your screen into smaller components. Each component would have its HTML, controller and model. Hence changes in the component remain localized. The concept of componentization, although old, was brought into the mainstream by React. With other modern frameworks like Angular and Vue falling in line, components have become the de-facto style of coding.

For smaller applications, this method seems to do wonder. Applications where state changes and operations are limited, having multiple components and keeping the communication between them minimal would do the trick. However, things start to fall apart real soon when complications arise where the number of components starts going north of 10. With more number of components and each one having its model, the same unpredictability creeps in. User actions in one component might (and in all probability would) have side-effects on models on other components as well. With those models being updated the view for other components would inadvertently be affected. These small changes, in turn, might change the model or view belonging to other components. Soon we would lose control and the fall in the same traps.



State



The state can be an ambiguous term and is often intangible. The result of all user and background actions cumulatively results in the "state" of the application. So the "state" can be attributed as the situation that the app is currently experiencing. Multiple aspects like User Actions, data, variables, views, error, properties, etc. all contribute towards the state of the application, which can be described best, as the "behaviour of the app".

E.g. let's say we need to display a list of books from an API on the click of a button. So we make an AJAX call to the server where the resource is hosted, parse the raw data to a list of JSON objects and then show that list as HTML unordered (<ul>) list in the page. Here the action was a click of a button, which resulted in making an AJAX call. And the result was the rendering of an unordered list in the page. Let's say once this is done, the user clicks on one of the books which add the book to the Wishlist of the user. Now if we need to represent this is in a data structure then the initial state of the application would look something like


State: {
    Books: [{
        Id: 1,
        Name: "1984",
        Author: "George Orwell"
    }, {
        Id: 2,
        Name: "Recursion",
        Author: "Blake Crouch"
    }, {
        Id: 3,
        Name: "The Suitable Boy",
        Author: "Vikram Seth"
    }],
    Wishlist: []
}

After the user click on one of the books then the state would look like


State: {
    Books:,
    Wishlist: [{
        Id: 1
    }]
}


State Management


In the majority of situations, developers don't resort to maintain an object to represent the state. Instead methods like in-memory variables, local storage, windows global object, etc. are used for storing the data received from API and then using some mechanism to store the Wishlist. State Management attempts to solve this problem by making this intangible state tangible by giving it a form that developers can interact against.

State Management makes the state of your app tangible in the form of a data structure that you can read and write to. It makes your 'invisible' state visible for you to work with.

- https://egghead.io/articles/what-is-state-why-do-i-need-to-manage-it


There are multiple libraries out there in the market which can act as a State Management library (probably redux is one of the most popular and most used). The bottom line is having a structure that one can look at and interact with, and the same structure reflects on the UI.​



Making the View Predictable


When you look at the DOM (the UI) multiple things like the data rendered on the screen, error messages, buttons, loader, etc. are part of the "state". Giving it a structure that and a tangible form is precisely the prime objective of a state management library.

The DOM should be a pure reflection of the state object.

In the world of state management if there is an error attribute in the JSON object, then it means there is an error message on the DOM. Following the same lines of thought, if we see a Loader attribute on the State, it would mean that there is a loader on the screen. Now let's think about the same thing from a reverse angle. It also implies that if we need to show an error message on the DOM, simply we need to add/modify the proper attribute on the singular state object. If you go back to our previous example where there were multiple ways to show a simple banner with a text on the screen, we solve that problem by simply exposing a banner attribute in the State object. When you need to show a banner change the state object. This is how the View becomes predictable. One need not be confused or look hard for how a user action needs to be performed or how and why a UI widget is being shown on the screen. It all boils down to the State Object.

Let's take a state object with a structure like this


{
    Profie: 
    {
        "Username": "patrick404" ,
        "ProfilePic": "https://somelocation.com"
    }
    Notifications: [{
        "Text": "You have a new offer from ABC.com",
        "Read": false
    }, 
    {
        "Text": "Your order no. 18882 has been dispatched",
        "Read": true
    }],
    SearchParameters: 
    {
        "SearchText": "JavaScript books",
        "PriceRange": "500-2500"
    },
    "Loader": 
    {
        "Loading": true,
        "Text": "Please wait while we fetch your products"
    },
    "Products": [],
    "Errors": []
}

Just by seeing this object, we can conclude the following information

  • Username and profile pic displayed in the profile section

  • User has two notifications, one of them is yet to read by the user

  • The user has searched for "JavaScript books" within a range of Rs. 500-2500

  • The products are still being fetched and meanwhile the user is seeing a loader

  • There are no errors on the screen

Now, what happens when the API call is over. If there was an error then from JavaScript you need to fill in the Errors array, and if the API was successful then fill the products with the data received. Along with toggle the Loader flag as well.

In case some issue happens over time, and you need to look at the Products which has been rendered on the screen you don't need to hunt for that information, it will always be in the state.

You avoid messy DOM manipulations and instead of focusing how to do something (like making DOM manipulations, 2-way binding etc.) you are focused on what to do (add/delete/modify attributes from the state). In other workds, you are towards a declarative and away from an imperative style of programming. This declarative style of updating the DOM by working on the state is what makes the View predictable.

SIGN UP AND STAY UPDATED!
  • th_grey
  • Grey Twitter Icon
  • Grey LinkedIn Icon
  • Grey Facebook Icon

© 2020 by DevCompost.

  • th
  • Twitter Social Icon
  • LinkedIn Social Icon
  • Facebook Social Icon