DeloitteEngineering Blog | Netherlands
15 May 2023

Implementing State Management in LWC (with Redux)

Introduction

State management is a way of tracking the state of an application at any given time. It also helps us with communication and sharing of data across components in a consistent manner. Additionally, it improves the quality of code through making it more readable and predictable.

To begin with, what is a state? A state is linked to a component. It holds the value of variables and when the state of a component changes, Lightning Web Components re-renders the UI. The state is retained throughout the lifespan of the component unless it is explicitly cleared.

In the context of Lightning Web Components, state management is not intrinsic, and therefore the need arises to incorporate the same, which makes an application faster, lighter, and easier to maintain. This is when Redux comes into the picture.

The objective of this article is to make the reader aware of the key capabilities of state management. We are going to cover state management, Redux, along with some examples of code.

Why should we use State Management?

Without the use of state management, data flows everywhere in an application, making it difficult to find a single source of truth. In a situation where we need to keep track of different interactions on a page, maintaining the code can be cumbersome. This might result in an inefficient process of development. As an application grows, the number of features that we need to keep track of increases as well. Consequently, this makes it cumbersome to keep track of the state that each feature has.

Data Everywhere chart

At Deloitte, some of our clients require a great deal of customization in Salesforce environments. In addition to configuring out-of the-box features, we also build non-declarative processes. This is where building Lightning Web Components (LWCs) comes into play. LWCs enable developers to build front ends tailored to Salesforce environments. When an application makes use of numerous components, we use the state to keep track of them.

For instance, when we create complex UI for use within a community, we typically call these modules. These modules typically hold the business logic and stitch all of the smaller reusable components together. Each of the smaller components may need to be registered with the state.

State Management and Redux

Tracking the state without a State Management Tool

We can track the state of an application without using a state management tool, such as Redux, by sharing the state across LWCs. In this example we have the following two components:

  1. sharedStateComponent : Stores the shared state and grant access across multiple components.
let _ stateData; 
 
const SharedStateObject = { 
  setData: (newVal) => { 
    _data = newVal; 
  }, 
  getData: () => { 
    return _stateData; 
  } 
}; 
 
Object.freeze(SharedStateObject); 
 
export { SharedStateObject }; 
  1. exampleComponent : imports the object that was created in the shared state component and use it to update the shared state upon checking the checkbox.
import { LightningElement, track } from "lwc"; 
import { SharedStateObject } from "./sharedStateComponent"; 
 
export default class MyComponent extends LightningElement { 
  @track stateData; 
 
  refreshStateData() { 
    return (this.stateData = SharedStateObject.getData()); 
  } 
 
  updateState(newValue) { 
    SharedStateObject.setData(newValue); 
    this.refreshStateData(); 
  } 
 
  updateStateToChecked() { 
    this.updateState(true); 
  } 
  updateStateToUnchecked() { 
    this.updateState(false); 
  } 
} 

Ticking on the checkbox will update the state accordingly as is illustrated below. The initial state is set to unchecked. On ticking the checkbox, the state changes to the checked state, and on further unticking the checkbox, the state changes to the unchecked state.

State

However, for clients with a large customer base, daily UI interactions with customers are needed to be tracked in an efficient manner. This requires the usage of a state management tool. This is where Redux comes into the picture.

Tracking the state by using Redux

Redux is a standalone library that can be used with LWCs, and other frameworks like React, Angular, Vue, Ember, and vanilla JS. It consists of three main components:

Store: Stores the complete state of an application. The application should be able to save and recover its current state.

Actions: The interaction between the application and the store. The application sends an action with some data. These actions are then handled by a so-called reducer.

Reducer: A function that takes the current state and transforms it into a new state.

“”

Let us illustrate this concept with a mock application. We have a webpage containing an overview of client information. It contains a modal that opens after the click of a button. It also contains a button that can download a file. The parts that need to be tracked are:

  1. The button for opening the modal, and
  2. The button that will track the progress of the download.
“”

The reducer will contain all the states of this application. For instance, there can be a state that tracks the loading of the webpage. In our case we track the state of the two buttons we mentioned above, and each of them is used as a state within the reducer. The button contains the state of opening the modal, and the download button contains the state that tracks the downloading progress of the file.

export const OverviewReducer = (state, action) => {
    // When initializing take a copy of the initial state
    // We need to clone it to avoid modifying the original structure in case we need to reset
    if(!state){
        state = deepClone(initialState);
    }

    const { payload } = action;
    let found;

    switch (action.type) {

        case 'SET_SELECTED_OVERVIEW_INFORMATION' :
            return { ...state, overviewInformation: payload };

Coming to actions, these are events that mutate the state within the application. For instance, the state for showing the information on the webpage has two fields, the type, and the payload. The value assigned to type should be a string with a name describing the action. The value assigned to payload contains data that can be mutated in the action.

export const setOpenModal = openModal => ({
    type: 'SET_OPEN_MODAL',
    payload: openModal
});
export const setDownloadedDocument = document => ({
    type: 'SET_DOWNLOAD_DOCUMENTS',
    payload: document
});

To build a feature together with redux, we need to create multiple LWCs. Each component has a different responsibility. The components need to pass data between each other. From an elevated level, the diagram below is created. Each component has a different responsibility. The redux component should return the correct states for all the LWCs. The parent component needs to make sure that the user gets redirected to the correct page. The page loads, depending on valid user details. If the user details are valid, then the property from the state changes to true and the child component will be rendered. Also, we have a modal that will render value A or B depending on the Redux property.

LWC flow 3

Conclusion

Redux is a good solution for managing the state in large scale applications, however, it is not suitable for all projects that contain LWCs. We need to assess whether the implementation of redux makes sense for the project, depending on the scope of the components and time constraints of the project. We have listed considerations in which case you might want to use state management or not.

When to use Redux

Application Size

Scalability

Single source of truth

Debugging

When NOT to use Redux

Small applications

Performance

Learning

Sources