Gaurav Kumar
Gaurav Kumar

Reputation: 1091

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.

Upvotes: 5

Views: 3236

Answers (1)

stafamus
stafamus

Reputation: 509

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.

Upvotes: 5

Related Questions