Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux + Normalizr : Adding and deleting normalized entities in Redux state

I have an API response which has a lot of nested entities. I use normalizr to keep the redux state as flat as possible.
For eg. the api response looks like below:

{
  "id": 1,
  "docs": [
    {
      "id": 1,
      "name": "IMG_0289.JPG"
    },
    {
      "id": 2,
      "name": "IMG_0223.JPG"
    }
  ],
  "tags": [
    {
      "id": "1",
      "name": "tag1"
    },
    {
      "id": "2",
      "name": "tag2"
    }
  ]
}

This response is normalized using normalizr using the schema given below:

const OpeningSchema = new schema.Entity('openings', {
    tags: [new schema.Entity('tags')],
    docs: [new schema.Entity('docs')]
});

and below is how it looks then:

{
  result: "1",
  entities: {
    "openings": {
       "1": {
          "id": 1,
          "docs": [1,2],
          "tags": [1,2]
       }
    },
    "docs": { 
      "1": { 
        id: "1",
        "name": "IMG_0289.JPG"
      },
      "2": { 
        id: "2",
        "name": "IMG_0223.JPG"
      }
    },
    "tags": {
      "1": {
          "id": 1,
          "name": "tag1"
      },
      "2": {
          "id": 2,
          "name": "tag2"
      }
    }
  }
}

The redux state now looks something like below:

state = {
  "opening" : {
      id: 1,
      tags: [1,2],
      docs: [1,2]
  },
  "tags": [
      {
          "id":1,
          "name": "tag1"
      },
      {
          "id":2,
          "name": "tag2"
      }
  ],
  "docs": [
      {
          "id":1,
          "name": "IMG_0289.JPG"
      },
      {
          "id":2,
          "name": "IMG_0223.JPG"
      }
  ]
}

Now if I dispatch an action to add a tag, then it adds a tag object to state.tags but it doesn't update state.opening.tags array. Same behavior while deleting a tag also.

I keep opening, tags and docs in three different reducers.

This is an inconsistency in the state. I can think of the following ways to keep the state consistent:

  1. I dispatch an action to update tags and listen to it in both tags reducer and opening reducer and update tags subsequently at both places.
  2. The patch request to update opening with tags returns the opening response. I can again dispatch the action which normalizes the response and set tags, opening etc with proper consistency.

What is the right way to do this. Shouldn't the entities be observing for changes to the related entities and make the changes itself. Or there are any other patterns that could be followed any such action.

like image 343
Gaurav Kumar Avatar asked Jun 06 '17 09:06

Gaurav Kumar


People also ask

What happens when Redux state changes?

The only way to update a state inside a store is to dispatch an action and define a reducer function to perform tasks based on the given actions. Once dispatched, the action goes inside the reducer functions which performs the tasks and return the updated state to the store. This is what Redux is all about.

What is normalized data Redux?

The basic concepts of normalizing data are: Each type of data gets its own "table" in the state. Each "data table" should store the individual items in an object, with the IDs of the items as keys and the items themselves as the values. Any references to individual items should be done by storing the item's ID.

Do you need to Keepis all component states in Redux store?

Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state. Using local component state is fine.


1 Answers

First to summarise how normalizr works: normalizr flattens nested API response to entities defined by your schemas. So, when you made your initial GET openings API request, normalizr flattened the response and created your Redux entities and the flattened objects: openings, docs, tags.

Your suggestions are viable, but I find normalizr's real benefit in separating API data from UI state; so I don't update the data in Redux store myself... All my API data are kept in entities and they are not altered by me; they are vanilla back-end data... All I do is to do a GET upon state changing API operations, and normalise the GET response. There is a small exception for DELETE case that I'll expand on later on... A middleware will deal with such cases, so you should use one if you haven't been using. I created my own middleware, but I know redux-promise-middleware is quite popular.

In your data set above; when you add a new tag, I assume you are making an API POST to do so, which in turn updates the back-end. Then, you should do another GET openings which will update the entities for openings and all its nested schemas.

When you delete a tag, e.g. tag[2], upon sending the DELETE request to the back-end, you should nullify the deleted object in your entities state, ie. entities.tags[2] = null before making the GET openings again to update your normalizr entities.

like image 184
stafamus Avatar answered Sep 21 '22 21:09

stafamus