问题
Is there any way to set new generic in 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.
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? For example, like below
const MemoComponent = <G extends string | number | object>React.memo<{value: G}>(({value}) => component)
// and use like this
<MemoComponent<number> />
回答1:
Update (credit goes to @Ryan1729's issue hint):
tl;dr:
Currently it is not possible to use generic type arguments in combination with 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
.
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:
// 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>
:
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.
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:
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;
来源:https://stackoverflow.com/questions/57477395/typescript-generic-class-equivalent-for-react-memo