Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to nest Reselect selectors?

I have a selector that returns an array. The elements in the array themselves have derived data. I essentially need a recursive memoization selector that returns a derived array composed of derived elements.

my current attempt is:

export const selectEntitesWithAssetBuffers = createSelector(
  [selectSceneEntities, getAssets],
  (entities, loadedAssets) => {
    return entities.map((entity) => {
      entity.buffers = entity.assets.map((assetName) => {
        return loadedAssets[assetName].arrayBuffer;
      })
      return entity;
    })
  }
)

My concerns here are anytime entities or loadedAssets change this will recompute the entire list. What I'm expecting to setup is something like a selectEntityWithBuffer that would get passed to the entities.map. Ideally, I want this to only recompute when an entity.assets array changes.

like image 831
kevzettler Avatar asked Sep 07 '17 23:09

kevzettler


Video Answer


2 Answers

Reselect allows you to provide custom equality definitions to your selectors.

import { defaultMemoize, createSelectorCreator } from 'reselect'

const compareByAssets = (a, b) => {
    return a.every((element, index) => {
        return element.assets === b[index].assets
    });
};

const createAssetsComparatorSelector = createSelectorCreator(
    defaultMemoize,
    compareByAssets
);

const selectSceneEntitiesByAssetsComparator = createAssetsComparatorSelector((state) => {
    //however you normally get entities for the pre-existing selectors
});

Now you can use this new selectSceneEntitiesByAssetsComparator in place of the previous selectSceneEntities in the above code you provided and it will only re-run when the equality check in compareByAssets fails.

Feel free to further update that comparator function if a strict comparison of assets === assets doesn't suite your needs.

like image 167
Jake Haller-Roby Avatar answered Oct 24 '22 09:10

Jake Haller-Roby


As a proof of concept, I'd try to provide loadedAssets object to the result function by bypassing reselect identity checks.

// Keep a private selector instance
let cachedSelector;

export const selectEntitesWithAssetBuffers = function(){
    // loadedAssets should be recalculated on each call?
    const loadedAssets = getAssets(arguments);

    // create selector on first call
    if(cachedSelector === undefined) {
        cachedSelector = createSelector(
            selectSceneEntities,
            entities => {
                return entities.map(entity => {
                    entity.buffers = entity.assets.map((assetName) => {
                        return loadedAssets[assetName].arrayBuffer;
                    })
                    return entity;
                })
            }
        )
    }

    // Return selector result
    return cachedSelector(arguments);
}
like image 32
Andrea Carraro Avatar answered Oct 24 '22 08:10

Andrea Carraro