Как создать пользовательскую функцию равенства с повторным выбором и Typescript? - PullRequest
2 голосов
/ 20 июня 2020

Стандартный селектор повторного выбора аннулирует его мемоизированное значение и пересчитывает его, если селекторы ввода не проходят строгую проверку равенства:

export const selectEmailsFromComments = createSelector(
  selectComments, // returns an array of comments
  comments => commentsToEmails(comments)
)

Поскольку комментарии являются массивом, а не примитивным значением, редукторы redux обычно создать новый фрагмент состояния, чтобы избежать побочных эффектов, похоже, что приведенное выше никогда не мемоизируется, потому что массив комментариев, возвращаемый selectComments, всегда будет иметь другую ссылку.

Чтобы решить эту проблему, можно создать Создатель настраиваемого селектора , например, для введения мелких проверок равенства:

const createShallowEqualSelector = createSelectorCreator(
  defaultMemoize,
  shallowEqual
)

export const selectEmailsFromComments = createShallowEqualSelector(
  selectComments, // returns an array of comments
  comments => commentsToEmails(comments)
)

Это работает, если комментарии действительно являются простыми объектами, и мы хотим пересчитывать электронные письма всякий раз, когда изменяется какой-либо из свойств комментариев.

Но что, если мы хотим только пересчитать электронные письма, если, например, количество комментариев изменилось? Как мы могли бы реализовать пользовательскую проверку равенства? Я ожидаю, что сработает следующее:

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

Однако это возвращает следующую ошибку Typescript для 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)

Как мне разрешить эту ошибку типа для настраиваемой функции равенства повторного выбора createSelector ?

1 Ответ

1 голос
/ 20 июня 2020

Следующее сработало для меня, но я не уверен, что это лучший способ.

import {
  createSelectorCreator,
  defaultMemoize,
} from 'reselect';

type IComment = { id: number };
type State = { comments: IComment[] };
type CompareFn = <T>(a: T, b: T, index: number) => boolean;
const createCustomEqualSelector = (equalFn: CompareFn) =>
  createSelectorCreator(defaultMemoize, equalFn);
const selectComments = (state: State) => state.comments;
const commentsEqualFn: CompareFn = (a, b, index) =>
  //need to cast it or get an error:
  //  Property 'length' does not exist on type 'T'
  ((a as unknown) as IComment[]).length ===
  ((b as unknown) as IComment[]).length;

export const selectEmailsFromComments = createCustomEqualSelector(
  commentsEqualFn
)(
  selectComments, // returns an array of comments
  (comments) => {
    console.log('calculating comments:', comments);
    return comments;
  }
);
selectEmailsFromComments({ comments: [{ id: 1 }] })
//this will log previous value because array length didn't change
console.log('memoized', selectEmailsFromComments({ comments: [{ id: 2 }] }))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...