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:
tags
reducer and opening
reducer and update tags subsequently at both places.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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With