Normalizing the Data
Let’s have a look at how we can use the `normalizr` library to normalize the data.
With the reducers updated and the action structure defined, we are left to extract the payload from the denormalized data we got from the server.
A simple approach might have a custom function that knows each API’s shape and normalizes the returned nested JSON into a flat structure with custom code.
Since this is quite a common practice, the normalizr
library can replace custom code. We can define the schema of the data coming from the server and have the normalizr
code turn our nested JSON into a normalized structure we can pass directly into our new UPDATE_DATA
action.
To use the library, we first define the schema for our data:
import { schema } from 'normalizr';
export const ingredient = new schema.Entity('ingredient');
export const recipe = new schema.Entity('recipe', {
ingredients: [ingredient]
});
Here we defined two entity types, a basic ingredient
and a recipe
containing an array of ingredient
objects.
The
normalizr
library allows for much more complex schemas. Consult the documentation for details.
Once we have obtained the data from the server and defined a schema for the data, we can use normalizr
to normalize the data automatically:
import { normalize } from 'normalizr';
const data = normalize(data, recipe);
The data
object will now hold two keys:
entities
: An object containing the normalized entities, arranged by entity typeresult
: The ID of the main object or array (if we passed multiple recipes as data)
Inside the entities
key, we will find the normalized data:
{
ingredients: {
5123: {
id: 5123,
name: 'Egg',
quantity: 2
}
},
recipes: {
63: {
id: 63,
name: 'Omelette',
favorite: true,
preparation: 'How to prepare...',
ingredients: [5123]
}
}
}
The normalizr
library did all the hard work for us. Now we can update our store by sending the relevant parts to each reducer:
dispatch({ type: 'SET_INGREDIENTS', payload: data.entities.ingredients });
dispatch({ type: 'SET_RECIPES', payload: data.entities.recipes});
With this approach, each reducer only has to handle its own entity types, making the reducer code simple and nondependent on other actions and entities.
It is best to keep the schemas of each entity in a separate (and reusable) place in your application. E.g., in a
lib/schema.js
file.
Get hands-on with 1400+ tech skills courses.