State management has become a critical task in modern frontend applications, especially in the decoupled and stateless architecture where the data is pulled from the server and stored in the browser, which will be later used for rendering the user interface and handling user interactions. In order to keep the application performant, it is very important how you handle the data. Servers return the data in a complex structure that can have duplicates, which makes them bulky, redundant, and extremely complex to process them. This data can be normalized to remove duplicates and simplify its structure, making it easier to process and analyze.

For example, consider this comment's API response structure.

const blogPosts = [
  {
    id: 'post-1',
    author: { id: "user-1", username: 'user1', email: '[email protected]' },
    body: '......',
    comments: [
      {
        id: 'comment1',
        author: { id: "user-2", username: 'user2', email: '[email protected]' },
        body: '.....'
      },
      {
        id: 'comment2',
        author: { id: "user-3", username: 'user3', email: '[email protected]' },
        body: '.....'
      }
    ]
  },
];

There are two major problems with this data.

This data can be normalizaed to make it is easier, to update, store, and modifiy the records.

This is similar to the CRUD (Create, Read, Update, and Delete) that we perform in the databases, but here because we are dealing with the local state, we don't create a record, rather we just update, store, or modify them.

How to normalize the data?

The process of normalizing the state is also similar to how we normalize the database table in the RDBMS.

A representation of table relation in database

For each entity, we want them to have a single source of truth, which means they can be updated in only one place and will be referenced in other places. We can adhere to this principle of RDBMS for normalizing the state's data.

Using this, we can create a byIds and allIds hierarchical structure. This hierarchical structure allows for efficient data retrieval and manipulation.

const blogPosts = {
  posts: {
    byIds: {
      post-1: {
        id: 'post-1',
        author: 'user-1',
        body: '......',
        comments: ['comment-1', 'comment-2'],
      },
      post-2: {
        id: 'post-2',
        author: 'user-2',
        body: '......',
        comments: [],
      },
    },
    allIds: ['post-1', 'post-2'],
  },
  comments: {
    byIds: {
      "comment-1": {
        id: 'comment-1',
        author: 'user-2',
        comment: '.....',
      },
      "comment-2": {
        id: 'comment-2',
        author: 'user-3',
        comment: '.....',
      },
    },
    allIds: ['comment-1', 'comment-2'],
  },
  users: {
    byIds: {
      "user-1": {
        id: 'user-1',
        username: 'user1',
        email: '[email protected]',
      },
      "user-2": {
        id: 'user-2',
        username: 'user-2',
        email: '[email protected]',
      },
      "user-3": {
        id: 'user-3',
        username: 'user-3',
        email: '[email protected]',
      },
    },
    allIds: ['user-1', 'user-2', 'user-3'],
  }
}

Here we have grouped each entity and converted it to an object, using the id (unique identifier) as the key and the entity as the value.

Also, in each entity group, we have the ids array, which makes it easier to iterate rather than retrieving the object keys every time.