Normalizing Responses

Learn how to normalize the responses using the normalizr library.

When we use the normalizr library in Redux, the API middleware can automatically apply the normalization on the received data:

import { normalize } from 'normalizr';

const {
  url,
  onSuccess,
  onFailure,
  method = 'GET',
  data,
  transformRequest,
  transformResponse,
  schema
} = action.payload;

const dataProperty = ['GET', 'DELETE'].includes(method) ? 'params' : 'data';

axios.request({
  url,
  method,
  [dataProperty]: data,
  transformRequest,
  transformResponse
})
  .then(({ data }) => dispatch(onSuccess(normalize(data, schema))))
  .catch(error => { /* handle errors */ });

Our action creators can define a schema to automatically convert a complex nested response. However, normalized responses usually consist of data about multiple entities and require either handling one response action in multiple reducers or, better, sending multiple actions to update different parts of the state according to the response from the server.

For example, this action creator defines a network call that will automatically normalize a complex response containing an array of movies, each having a list of reviews and actors:

// Passing a normalizr schema in an action creator
import { moviesSchema } from '../schemas';

const fetchMovies = () => ({
  type: API,
  payload: {
    url: '/movies',
    schema: moviesSchema,
    onSuccess: ({ entities, result }) => [
      setMovies(result),
      setAuthors(entities.authors),
      setReviews(entities.reviews)
    ]
  }
});

You might notice that the onSuccess property now contains a function that returns an array of actions instead of a single action creator. While we can easily add support for this functionality in our API middleware, a more generic solution would be to add new middleware to the stack. This new middleware will catch all actions that are of type Array and dispatch them one at a time:

//Middleware to handle arrays of actions
const actionArrayMiddleware = ({ dispatch }) => next => action => {
  if (Array.isArray(action)) {
    action.forEach(dispatch);
    return;
  }

   next(action);
};

export default actionArrayMiddleware;

Get hands-on with 1200+ tech skills courses.