如何使用 reselect 和 Typescript 创建自定义相等函数?

Tom*_*Tom 6 typescript reactjs redux reselect

如果输入选择器未通过严格的相等性检查,标准重新选择选择器将使其记忆值无效并重新计算它:

export const selectEmailsFromComments = createSelector(
  selectComments, // returns an array of comments
  comments => commentsToEmails(comments)
)
Run Code Online (Sandbox Code Playgroud)

由于注释是一个数组而不是原始值,并且 redux 缩减器倾向于创建一个新的状态以避免副作用,因此上面的内容似乎永远不会真正记住,因为返回的注释数组selectComments将始终具有不同的引用。

为了解决这个问题,可以创建一个自定义选择器创建者,例如引入浅层相等检查:

const createShallowEqualSelector = createSelectorCreator(
  defaultMemoize,
  shallowEqual
)

export const selectEmailsFromComments = createShallowEqualSelector(
  selectComments, // returns an array of comments
  comments => commentsToEmails(comments)
)
Run Code Online (Sandbox Code Playgroud)

如果评论确实是简单的对象,并且我们希望在任何评论的 props 发生更改时重新计算电子邮件,则此方法有效。

但是,如果我们只想重新计算电子邮件(例如评论数量发生变化)怎么办?我们如何实现自定义相等性检查?我希望以下内容能够发挥作用:

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)
)
Run Code Online (Sandbox Code Playgroud)

然而,这会为 defaultMemoize 返回以下 Typescript 错误:

(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)
Run Code Online (Sandbox Code Playgroud)

如何解决自定义重新选择 createSelector 相等函数的此类型错误?

HMR*_*HMR 3

以下对我有用,但我不确定这是否是最好的方法。

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 }] }))
Run Code Online (Sandbox Code Playgroud)