Transforming state per component

Every component you write has different needs. Some might show data in the same format your as API, while others might need to amend, blend or selectively combine data for its UI.

According to our general rule, a component 'should never transform data itself' as we 'should never write logic inside a UI component'.

Here's why shouldn't you transform data in a component:

  • Coupling your component to a particular data structure means your component isn't reusable
  • The transformation itself is single-use (and may well be copied to another component)
  • The transformation is harder to test inside a component
  • It clutters components, making them far less readable

We get around this problem by using reselect, which sits in between your Redux store and your components to query and transform data.

Transforming state via reselect

Reselect is a powerful library which allows you to build a query and transform pipeline for your components. It allows you to:

  1. Create selectors - functions which select and return pieces of state
  2. Combine selectors to return multiple resources at once
  3. Modify data based on existing state

This allows you to process state in any manner for your components. As a bonus, reselect also memoizes each function's outputs for increased performance.

Say we have a route with the path /posts/:slug, and we're using redux-router to store route params in global state. We can then create a selector to query for the current post:

currentPageSelector = (state) => {
  // immutable.js uses "get" to query maps:
  // https://facebook.github.io/immutable-js/docs/#/Map/get
  return state.pages.get(state.router.params.slug, new PostRecord());
};

We can then combine this into another selector to return the current page and comments

// This returns an immutable map of all comments
allComments = state => state.comments;

currentPageWithComments = createSelector(
  [currentPageSelector, allComments],
  (page, comments) => {
    // We're sticking to our hard and fast rule of storing data in a map
    // keyed by IDs commonly used in selecting.
    // In this example we're storing all comments in a map keyed by page ID.
    let pageComments = comments.get(page.id, new Immutable.Map());
    return {
      page,
      comments: pageComments
    };
  }
);

This moves all state logic into separate reusable, performant, testable functions. For more information see the reselect documentation.