Как вложить селекторы повторного выбора?

у меня есть селектор, который возвращает массив. Сами элементы массива имеют производные данные. Мне по существу нужен рекурсивный селектор memoization, который возвращает производный массив, состоящий из производных элементов.

моя текущая попытка:

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

мои проблемы здесь в любое время entities или loadedAssets изменить это будет пересчитать весь список. То, что я ожидаю настроить, - это что-то вроде selectEntityWithBuffer это будет передано в entities.map. В идеале, я хочу этого только пересчитать, когда entity.assets изменения массива.

3 ответов


Reselect позволяет предоставлять пользовательские определения равенства для ваших селекторов.

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
});

теперь вы можете использовать этот новый selectSceneEntitiesByAssetsComparator вместо предыдущего selectSceneEntities в приведенном выше коде вы указали, и он будет повторно запущен только при проверке равенства compareByAssets не удается.

не стесняйтесь, чтобы обновить эту функцию компаратора, если строгое сравнение assets === assets не соответствует вашим потребностям.


в качестве доказательства концепции я бы попытался предоставить loadedAssets объект функции результата по обход повторного выбора проверки личности.

// 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);
}

получение более глубокой memoization, чем то, что у вас есть, является сложной проблемой, потому что Reselect действительно не поддерживает передачу аргументов селекторам. Если вы возвращаете массив из селектора, и ввод, используемый для создания этого массива, изменился, это своего рода предполагаемое поведение от Reselect, которое вам нужно будет пересчитать. См.совет в readme для динамических аргументов.