Digging into Reselect

export const coolThings = createSelector(
  [things],
  (_things) => things.filter((thing) => thing.isCool)
); 

What is a Selector?

A pure, memoized function that takes a set of inputs and returns an output

Why Selectors?

  • Compute derived data which should not be stored in Redux
  • Encompass and isolate data transformation rules
  • Avoid costly recomputes by leveraging memoization

Derived Data

const cartItems = (state) => state.cart.items;

const getTotal = createSelector(
  [cartItems],
  (_cartItems) => {
    return _cartItems.reduce((prev, item) => {
      return prev + item.count * item.amount;
    }, 0);
  }
);
  • Creating new state derived on current state
  • Should not store calculated total in state
  • Should instead be always calculated based on state changes

Data Transformation

const inventoryItems = (state) => state.inventory.items;

const getInventory = createSelector(
  [inventoryItems],
  (_inventoryItems) => {
    return _inventoryItems.map((item) => {
      item.isSoldOut = item.amount === 0;
      item.isAlmostSoldOut = item.amount > 0 && item.amount < 4;
      item.isNoLongerAvailable = item.amount === -1;
      return item;
    });
  }
);
  • Transforming state through application of business rules

Leverage Memoization

const inputs = (_inputs) => _inputs;

const expensiveComputation = createSelector(
  [inputs],
  (_inputs) => {
    console.log('COMPUTING');
    return doSomethingToDataThatTakesALongTime(_inputs);
  }
);

expensiveComputation(data); // COMPUTING
expensiveComputation(data); // no console log
  • Selector functions are memoized/cached
  • If same inputs, do not re-calculate, provide previous output
  • Memoized 1 value deep (can be customized if more needed)

Use Anywhere!

const exampleActionCreator = (type) => {
  return (dispatch, getState) => {
    ...

    if (type == ACTION_TYPES.TOGGLE_HEADER) {
      prefService.setPreferences(
        'events', 
         getReconPreferences(getState())
      )
    }

    ...
  };
};
  • selectors have literally nothing to do with ember or redux or ember-redux
  • Use them wherever you have state and need to know something about it
  • You might find them useful in actionCreators for instance

Memo Demos

Fully Utilizing Memoization

  • If a selector takes 5 inputs, that is 5 different things that, when changed, can cause the recompute of the selector
  • If possible, break selectors down into smaller selectors.
    • Build one that takes the 3 pieces of fairly stable un-changing data and builds an interim data structure
    • Make the output of that the input into the selector that takes the other two pieces of more variable data
    • This way the processing of the the stable data happens infrequently and does not need to be repeated

Memo Perf Demo

reselect

By David Bashford

reselect

  • 600