Skip to content
A state selector to optimize the usage of React, Redux, Reselect & Normalizr.
Branch: master
Clone or download
Latest commit 131fc37 May 30, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci circle-ci code-climate integration May 19, 2018
fixtures add snapshot tests May 8, 2018
src
.babelrc create build logic May 6, 2018
.coveralls.yml
.gitignore
.prettierrc create build logic May 6, 2018
.release-it.json
CODE_OF_CONDUCT.md
LICENSE
README.md
package.json
paths.js create build logic May 6, 2018
webpack.config.js externalize packages May 6, 2018
yarn.lock

README.md

Renorm

A state selector to optimize the usage of React, Redux, Reselect & Normalizr.

CircleCI Status Coverage Status Maintainability minzipped size npm version GitHub license

Table of Contents

Motivation

An avoidable re-render happens when a React component receives a shallow copy of one of it's properties, however the object didn't "really" change. Meaning A deep comparison of all of the object's primitives would find them identical. This causes a React component to run it's render method needlessly, since the render result would be identical to the last one.

Solution

Documentation

Installation

yarn add renorm
npm install --save renorm

Features

  • Discovers entities used in your selector automatically.
  • Significant performance boost when selecting a list of entities.
  • Developer friendly syntax.

How It Works

renorm(inputSelector, schema) When you create a renorm selector this is what happens:

  1. A new reselect selectorCreator is created with the schema you provided. Renorm uses a customized version of Reselect's defaultMemoize optimized for normalizr entities.
  2. Renorm traverses the schema you provided and saves all unique entities found.
  3. Renorm creates an entities selector with only the relevant schema entities
  4. Renorm now uses the Reselect selector creator from #1 which uses input, schema and relevant entities to invoke Normalizr's denormalize method. This is the return value when invoking renorm

Memoization Strategy

Renorm has a two phase memoization strategy

Basic Memoization. Based on Reselect's existing defaultMemoize

  1. Check arguments length and null check
  2. Shallow equality of of the input ids and each one of the relevant entity maps (stocks, companies, etc.) if all the checks return true the memoized version is returned, otherwise the function is invoked. This the exact same process Reselect already does by default.

if the function is invoked Renorm switches to advanced memoization

Advanced Memoization

For each the denormalized object, check all underlying entities:

  1. If all underlying entities are shallowly equal to the previous ones, return previous denormalized object.
  2. Else return the denormalized object
  3. Cache results for next run

The big advantage here is that only newly returned objects will trigger React's render. Without Renorm's advanced memoization every item on the list would trigger React's render, even if you only a single entity was changed!

Examples

Basic Usage

// schema
import { schema } from 'normalizr';
const stockSchema = new schema.Entity('stocks');
const companySchema = new schema.Entity('companies', {
  stock: stockSchema,
});
export const Schemas = {
  STOCK: stockSchema,
  STOCK_ARRAY: [stockSchema],
  COMPANY: companySchema,
  COMPANY_ARRAY: [companySchema],
};

// redux state
const state = {
  companyIds: ['COMP_A', 'COMP_B', 'COMP_C' /*...*/],

  entities: {
    companies: {
      /*company entities...*/
    },
  },
  stocks: {
    /*stock entities...*/
  },
};

//renorm selector
import renorm from 'renorm';
const getCompanyIds = (state) => state.companyIds;
const getCompanies = renorm(getCompanyIds, Schemas.COMPANY_ARRAY);

For comparison, here's the same selector without using renorm:

import { denormalize } from 'normalizr';
import { createSelector } from 'reselect';
const getCompanyIds = (state) => state.companyIds;
const getCompanyEntities = (state) => state.entities.companies;
const getStockEntities = (state) => state.entities.stocks;
export const getCompanies = createSelector(
  getCompanyIds,
  getCompanyEntities,
  getStockEntities,
  (stockList, companies, stocks) =>
    denormalize(stockList, Schemas.COMPANY_ARRAY, {
      companies,
      stocks,
    })
);

Performance

Options

Renorm's third parameter is an optional options object that contains the following props:

Name Type Default Value Description
entitiesPath string "entities" Path to the entities object in the state
process function (result) => result A function to perform mutation operations before results are returned

Dependencies

License

MIT

You can’t perform that action at this time.