什么是' T '类型的参数在ReactElement?



ReactElement中的T类型参数是什么?从主体可以看出它是用于type

interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}

但我不确定我将在那里放什么。例如,如果我有一个高阶组件(HOC),它只转发引用:

import { ComponentType, forwardRef, NamedExoticComponent, PropsWithoutRef, ReactElement, Ref, RefAttributes } from 'react';
import { render } from '@testing-library/react-native';
import { Text, TextProps } from 'react-native';
/**
*
* @param Component component to wrap
* @param options options for the HoC building
* @typeParam P the exposed props of the higher order component
* @typeParam Q the props for the wrapped component
* @typeParam T type for ref attribute of the wrapped component
* @typeParam O options for the HoC building
* @returns A named exotic componentwith P props that accepts a ref
*/
function hoc<P, Q, T, O = {}>(Component: ComponentType<Q>, options?: O): NamedExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
function wrapped(props: P, ref: Ref<T>): ReactElement<Q> {
// The unknown as Q here is an example, but P and Q can be different.
const componentProps: Q = props as unknown as Q;
return <Component {...componentProps as Q} ref={ref} />
}
const displayName =
Component.displayName || Component.name || "AnonymousComponent";
wrapped.displayName = displayName;
return forwardRef<T, P>(wrapped);
}
describe("hoc", () => {
it("should work with text", () => {
const HocText = hoc<TextProps, TextProps, typeof Text>(Text);
const { toJSON } = render(<HocText>simple string</HocText>);
const { toJSON: expectedToJSON } = render(<Text>simple string</Text>)
expect(toJSON()).toStrictEqual(expectedToJSON())
});
});

上面的工作,但什么是更严格的版本,我可以放入ReactElement<Q,???>,但仍然使它编译?

T要么是一个内置的JSX元素,要么是一个自定义的功能/类组件。对于React的东西,像div,input等。读取*createElement()的参数

type: type参数必须是一个有效的React组件类型。例如,它可以是一个标签名称字符串(如'div'或'span'),或一个React组件(一个函数,一个类,或一个特殊的组件,如Fragment)。

我相信像这样的东西应该适合你的需要。我改变了一些东西
  • ReactElement<Q>->ReactElement<Q, typeof Component>
  • P extends Q通用
    • 这个完全是可选的,因为React无论如何都会丢弃无关的属性
  • wrapped函数到wrapped匿名函数将允许我们输入这个
    • 允许wrapped.displayName = 'foobar'仍然工作,但不允许wrapped.foobar = true之类的东西
function hoc<P extends Q, Q, T, O = {}>(
Component: ComponentType<Q>,
options?: O
): NamedExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
const wrapped: ForwardRefRenderFunction<T, P> = (
props: P,
ref: Ref<T>
): ReactElement<Q, typeof Component> => {
return <Component {...props} ref={ref} />;
};
const displayName =
Component.displayName || Component.name || 'AnonymousComponent';
wrapped.displayName = displayName;
return forwardRef<T, P>(wrapped);
}

现在,据我所知,键入任何功能组件的返回值是没有意义的,因为TypeScript不会静态分析JSX,除非知道它是一个JSX元素。

const test = <Component {...props} ref={ref} />
// ^ regardless of any component, as long it is is JSX it will
// be of type `JSX.Element`, which is just `React.ReactElement<any, any>`

所以你最终可以使用不正确的ReactElement…

const wrapped: ForwardRefRenderFunction<T, P> = (
props: P,
ref: Ref<T>
): ReactElement<Q, typeof Component> => {
return <></>; // no errors?
}

所以这也是合适的:

function hoc<P extends Q, Q, T, O = {}>(
Component: ComponentType<Q>,
options?: O
): NamedExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
const wrapped: ForwardRefRenderFunction<T, P> = (
props: P,
ref: Ref<T>
): JSX.Element => {
return <Component {...props} ref={ref} />;
};
const displayName =
Component.displayName || Component.name || 'AnonymousComponent';
wrapped.displayName = displayName;
return forwardRef<T, P>(wrapped);
}
// And even more strict
function hoc<
P extends Q,
Q extends {},
T extends string | JSXElementConstructor<any> =
| string
| JSXElementConstructor<any>,
O = {}
>(
Component: ComponentType<Q>,
options?: O
): NamedExoticComponent<PropsWithoutRef<P> & RefAttributes<T>> {
const wrapped: ForwardRefRenderFunction<T, P> = (
props: P,
ref: Ref<T>
): JSX.Element => {
return <Component {...props} ref={ref} />;
};
const displayName =
Component.displayName || Component.name || 'AnonymousComponent';
wrapped.displayName = displayName;
return forwardRef<T, P>(wrapped);
}

无论如何,这里有一个很好的问答:何时使用JSX。Element vs ReactNode vs ReactElement?

您也可以使用React.createElement而不是JSX,但是除了输入深度之外没有任何区别。此外,这将比它的价值更令人头痛,因为这些都是针对您而不是任何最终用户的实现细节。

最新更新