A standard reselect selector invalidates its memoized value and recomputes it if the input selectors fail a strict equality check:
export const selectEmailsFromComments = createSelector(
selectComments, // returns an array of comments
comments => commentsToEmails(comments)
)
Since the comments are an array and not a primitive value, and redux reducers tend to create a new piece of state to avoid side effects, the above seems to never actually memoize because the comments array returned by selectComments
will always have a different reference.
To resolve this, one can create a custom selector creator, e.g. to introduce shallow equality checks:
const createShallowEqualSelector = createSelectorCreator(
defaultMemoize,
shallowEqual
)
export const selectEmailsFromComments = createShallowEqualSelector(
selectComments, // returns an array of comments
comments => commentsToEmails(comments)
)
This works if the comments indeed are simple objects and we want to recompute the emails whenever any of the comments' props changed.
But what if we only want to recompute the emails if e.g. the number of comments changed? How could we implement a custom equality check? I would expect the following to work:
type ComparisonFunction<B extends object = {}> = (prev: B, next: B, index: number) => boolean
const createCustomEqualSelector = <B extends object = {}>(
equalFn: ComparisonFunction<B>
) => createSelectorCreator<ComparisonFunction<B>>(defaultMemoize, equalFn)
const commentsEqualFn = (a: IComment[], b: IComment[], index: number) =>
a.length === b.length
export const selectEmailsFromComments = createCustomEqualSelector(
commentsEqualFn
)(
selectComments, // returns an array of comments
comments => commentsToEmails(comments)
)
However this returns the following Typescript error for defaultMemoize:
(alias) function defaultMemoize<F extends Function>(func: F, equalityCheck?: (<T>(a: T, b: T, index: number) => boolean) | undefined): F
import defaultMemoize
Argument of type '<F extends Function>(func: F, equalityCheck?: (<T>(a: T, b: T, index: number) => boolean) | undefined) => F' is not assignable to parameter of type '<F extends Function>(func: F, option1: ComparisonFunction<B>) => F'.
Types of parameters 'equalityCheck' and 'option1' are incompatible.
Type 'ComparisonFunction<B>' is not assignable to type '<T>(a: T, b: T, index: number) => boolean'.
Types of parameters 'prev' and 'a' are incompatible.
Type 'T' is not assignable to type 'B'.ts(2345)
How would I resolve this type error for a custom reselect createSelector equality function?