Redux - Selectors & Reselect

Selectors compute derived data from the store. Memoized selectors avoid recomputation unless inputs change.

Try it: Reselect memoization

View source
const { createStore } = Redux;
const { createSelector } = Reselect;
const initial = { items: Array.from({length:500}, (_,i)=>({ id:i, done:i%3===0 })) , filter:'all' };
function reducer(s=initial, a){
  if (a.type==='filter/set') return { ...s, filter:a.payload };
  if (a.type==='toggle') return { ...s, items: s.items.map(x => x.id===a.payload ? { ...x, done:!x.done } : x) };
  return s;
}
const store = createStore(reducer);
const selectItems = s => s.items;
const selectFilter = s => s.filter;
let computeCount = 0;
const selectVisible = createSelector([selectItems, selectFilter], (items, filter) => {
  computeCount++;
  if (filter==='done') return items.filter(x=>x.done);
  if (filter==='todo') return items.filter(x=>!x.done);
  return items;
});
function render(){
  const state = store.getState();
  const visible = selectVisible(state);
  document.getElementById('sel-out').textContent = `visible:${visible.length} computeCount:${computeCount}`;
}
store.subscribe(render); render();
// UI wiring
document.getElementById('sel-all').onclick = () => store.dispatch({type:'filter/set', payload:'all'});
document.getElementById('sel-done').onclick = () => store.dispatch({type:'filter/set', payload:'done'});
document.getElementById('sel-todo').onclick = () => store.dispatch({type:'filter/set', payload:'todo'});
document.getElementById('toggle-first').onclick = () => store.dispatch({type:'toggle', payload:0});

Syntax primer

  • createSelector(inputs..., resultFn) memoizes results until inputs change.
  • Use small input selectors (s => s.items) combined by a memoized selector.