简体   繁体   English

等效于 React.memo 的 Typescript 泛型类

[英]Typescript generic class equivalent for React.memo

Is there any way to set new generic in React.memo?有没有办法在 React.memo 中设置新的泛型?

For example, for class components, I can use例如,对于类组件,我可以使用

// set generic
class GenericClass<G extends string | number> extends PureComponent {}

// and use like this
<GenericClass<number> />

In the below scenario, I want to make type G generic.在下面的场景中,我想让 G 类通用。

type G = string;
const MemoComponent = memo<{ value: G; onValueChange: (newValue: G) => void }>(
  ({ value, onValueChange }) => {
    return (
      <select
        onClick={() => {
          // do calculation
          onValueChange(value);
        }}
      >
        Button
      </button>
    );
  }
);

I want to know is there any way to set generic in React.memo too?我想知道有没有办法在 React.memo 中设置泛型? For example, like below例如,如下所示

const MemoComponent = <G extends string | number | object>React.memo<{value: G}>(({value}) => component)

// and use like this
<MemoComponent<number> />

I had the same issue and solved like this.我有同样的问题并像这样解决了。

    interface ISomeComponentWithGenericsProps<T> { value: T; } 

    function SomeComponentWithGenerics<T>(props: ISomeComponentWithGenericsProps<T>) {
      return <span>{props.value}</span>;
    }

    export default React.memo(SomeComponentWithGenerics) as typeof SomeComponentWithGenerics;

Update (credit goes to @Ryan1729's issue hint): 更新(信用转到@ Ryan1729的问题提示):

tl;dr: TL;博士:

Currently it is not possible to use generic type arguments in combination with React.memo . 目前, 不能泛型类型参数React.memo结合使用。 TypeScript cannot retain the free generic type parameter of a passed in generic component (which essentially is a functional interface), so we have to provide a concrete one, when we invoke React.memo . TypeScript不能保留传入的泛型组件(实质上是一个功能接口)的免费泛型类型参数,因此当我们调用React.memo时,我们必须提供一个具体的React.memo

Explanation: 说明:

A general example of the issue is given in above issue : 上述问题给出了该问题的一般示例:

function identity<T>(arg: T) { return arg; }
function memoize<F extends (...args: unknown[]) => unknown>(fn: F): F { return fn; }

// memid: unknown => unknown
// desired: memid<T>(T) => T
const memid = memoize(identity);

Now, when you look at the type signature of React.memo , you can see that it is a HOC that returns some sort of internal Component type with a generic type argument resulting from your props of the input component: 现在,当您查看React.memo的类型签名时,您可以看到它是一个HOC ,它返回某种内部Component类型,其中包含由输入组件的props产生的泛型类型参数:

// Component: SFC<P> and NamedExoticComponent<P> are nothing more than 
// functional generic interfaces, we can interpret `React.memo` as a 
// generic higher order function.

function memo<P extends object>(Component: SFC<P>, ...): NamedExoticComponent<P>;

interface ExoticComponent<P = {}> {
  ...
  (props: P): (ReactElement|null);
  ...
}

Unfortunately, TypeScript cannot retain the free type parameter resulting from SFC<P> : 不幸的是,TypeScript无法保留SFC<P>产生的自由类型参数:

const Comp = <T extends {}>(props: {foo: T}) => {...};
// we do not provide concrete type parameter for Comp
const HOC = React.memo(Comp);
// doesn't work :/
const App = <HOC<string> />;

Alternatives 备择方案

1.) You could wrap memo in another HOC, if you really wanted, but you cannot set the generic type parameter in JSX. 1.)如果你真的想要,可以将memo包装在另一个HOC中,但是你不能在JSX中设置泛型类型参数。

type MemoComponentProps<G> = { ... }

const createMemoComponent = <G extends string | number | object>() =>
  React.memo((props: MemoComponentProps<G>) => { ... })

const MemoComponent = createMemoComponent<number>();

2.) Do not use React.memo and switch to useMemo Hook inside your input component. 2.)不要使用React.memo并在输入组件中切换到useMemo Hook。

You can write your own wrapper not to use type assertion(as) :您可以编写自己的包装器以不使用 type assertion(as) :

const genericMemo: <T>(component: T) => T = memo

function GenericComponent<Value>({props: PropsWithGeneric<Value>}) {
  return <div />
}

export const GenericMemoComponent = genericMemo(GenericComponent)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM