dai-shi/proxy-memoize

Example of composing selectors

Noitidart opened this issue · 5 comments

I am a user of reselect and was looking for alts due to the limitations. I was hoping to do a PR to show one of the common cases from reselect. Composing selectors. The current reselect example in our readme, does all three steps in the totalSelector. In our app, we use the taxSelector and subtotalSelector in other places too, it just happens to also be used in totalSelector. Is this the right way to compose selectors:

const shopItemsSelector = state => state.shop.items;
const taxPercentSelector = state => state.shop.taxPercent;

const subtotalSelector = memoize(state => {
  const items = shopItemsSelector(state);
  return items.reduce((acc, item) => acc + item.value, 0),
});

const taxSelector = memoize(state => {
  const subtotal = subtotalSelector(state);
  const taxPercent = taxPercentSelector(state);
  return subtotal * (taxPercent / 100)
});

const totalSelector = memoize(state => {
  const subtotal = subtotalSelector(state);
  const tax = taxSelector(state);
  return { total: subtotal + tax }
});

So in this rewrite, we can use the subtotalSelector anywhere we need, but also in another memoized selector. Same with taxSelector. Does this example look good, for me to add to the readme as "composed selectors"? Or any better design to compose?

Thanks for opening this up.
The mental model is actually quite different from reselect, and that'd be the hardest part to educate the usage.

Now, basically, if your final product is totalSelector, you don't need any composition.
reselect requires to compose selectors because it need to know what is changed.
proxy-memoize tracks the property usage, so intermediate (unexposed) memoized selectors are just overheads.

const totalSelector = memoize(state => {
  const shopItems = state.shop.items;
  const taxPercent = shop.taxPercent;
  const subtotal = shopItems.reduce((acc, item) => acc + item.value, 0),
  const tax = subtotal * (taxPercent / 100)
  return { total: subtotal + tax }
});

If you need to structure selectors for organizing purpose (not for performance but for readability) feel free to do it.
If some of selectors are really heavy we can compose selectors to avoid re-computation, but in reselect use cases, it's very rare.

Pinning @theKashey here who developed https://github.com/theKashey/memoize-state which is a prior art of this lib, and he might be able to explain this thing well. (as I'm not a reselect user.)

Thank you very much for taking your time to share your thoughts here! I will work to make this help more than just myself. And an early thank you to @theKashey as well.

Hey Hey. So combining reselect approach with tracking is not working that great, as well as with any other "nested" memoization, so it's really easier to do it some another way, for example flatten like @dai-shi proposed.

There are ways, and proxy-memoize implemented a few, but it's really safer not to overuse tracking, and just do something it can work with without using any extra magic, or your own brainpower.

Thank you @theKashey! The ways you mention I'm not familiar with it. Anyone know of a easily digestable list I can read these in?

Closing this as it seems no fix stuff. Thanks for leaving open this long!