Vulcan often use utils from 'utils/selectors' eg: from import { selectBulkFactory } from 'utils/selectors'; That's the available utils: import { isObject } from 'lodash-es'; import { createSelector, createSelectorCreator, lruMemoize, weakMapMemoize } from 'reselect'; import type { RootState } from 'store/types'; export interface Entity { id: number | string; [key: string]: any; } // memoization is not needed here export const selectOneFactory = (sliceName: string) => (state: any, id: E['id']) => state[sliceName][id] as E | undefined; // memoize only the last result export const selectAllFactory = (sliceName: string) => createSelector( (state: any) => state[sliceName], state => Object.values(state) as E[], { memoize: lruMemoize, argsMemoize: lruMemoize, } ); // WeakMap based cache with infinite cache size. Cache results will be kept in memory as long as references // to the arguments still exist, and then cleared out as the arguments are garbage-collected. // we use custom resultEqualityCheck to sahllow compare the array of entities export const selectBulkFactory = (sliceName: string) => createSelector( [(state: any) => state[sliceName], (state: any, ids: E['id'][]) => ids], (state, ids) => (ids || []).map(id => state[id]) as E[], { memoize: weakMapMemoize, memoizeOptions: { resultEqualityCheck: arrayShallowEqual, }, argsMemoize: weakMapMemoize, } ); function arrayShallowEqual(a: Entity[], b: Entity[]) { if (!Array.isArray(a) || !Array.isArray(b)) { return a === b; } if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) { return false; } } return true; } export function asSelector( fn: (state: RootState, ...args: Args) => Selected, ...args: Args ) { return (state: RootState) => fn(state, ...args); } // this aims to provide the behavior of createCachedMapSelector in the monolith export const createResultCompareSelector = createSelectorCreator({ memoize: weakMapMemoize, memoizeOptions: { resultEqualityCheck: shallowEqual }, }); export function shallowEqual(values: unknown, newValues: unknown): boolean { if (values === newValues) { return true; } const valuesEmpty = !values; const newValuesEmpty = !newValues; if (valuesEmpty && newValuesEmpty) { return true; } if (newValuesEmpty !== valuesEmpty) { return false; } if (!isObject(values) || !isObject(newValues)) { return values === newValues; } const valueKeys = Object.keys(values) as (keyof typeof values)[]; const valuesSize = valueKeys.length; const newValuesSize = Object.keys(newValues).length; if (valuesSize !== newValuesSize) { return false; } for (const key of valueKeys) { if (values[key] !== newValues[key]) { return false; } } return true; }