Kiedy używać JSX.Element vs ReactNode vs ReactElement?
Obecnie migruję aplikację Reactową do maszynopisu. Jak na razie działa to całkiem dobrze, ale mam problem z typami zwrotnymi moich render
funkcji odpowiednio moich komponentów funkcyjnych.
Do tej pory zawsze używałem JSX.Element
jako typu return, teraz to już nie działa, jeśli komponent zdecyduje się Nie renderować cokolwiek, tzn. zwrócić null
, ponieważ null
nie jest poprawną wartością dla JSX.Element
. To był początek mojej podróży, ponieważ teraz Szukałem web i okazało się, że należy użyć ReactNode
zamiast, który obejmuje również null
, a także kilka innych rzeczy, które mogą się zdarzyć. To wydawało się być lepszym zakładem.
Jednak teraz przy tworzeniu komponentu funkcyjnego TypeScript narzeka na typ ReactNode
. Ponownie, po kilku poszukiwaniach znalazłem, że Dla komponentów funkcyjnych należy użyć ReactElement
. Jeśli jednak to zrobię, problem ze zgodnością zniknie, ale teraz TypeScript znowu narzeka na to, że null
nie jest poprawną wartością.
Więc, krótko mówiąc, mam trzy pytania:]}
- Jaka jest różnica między
JSX.Element
,ReactNode
iReactElement
? - dlaczego metody
render
składników klasy zwracająReactNode
, ale składniki funkcji zwracająReactElement
? - Jak to rozwiązać w odniesieniu do
null
?
2 answers
ReactElement to obiekt o typie i właściwościach.Jaka jest różnica między JSX.Element, ReactNode i ReactElement?
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
ReactNode to ReactElement, ReactFragment, ciąg znaków, Liczba lub tablica ReactNodes, null, undefined, lub boolean:
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
JSX.Element jest Reaktelementem, A Typ generyczny dla właściwości i typ jest dowolny. Istnieje, ponieważ różne biblioteki mogą zaimplementować JSX na swój sposób, dlatego JSX jest globalna przestrzeń nazw, która jest następnie ustawiana przez Bibliotekę, react ustawia ją w następujący sposób:
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
}
}
Przez przykład:
<p> // <- ReactElement = JSX.Element
<Custom> // <- ReactElement = JSX.Element
{true && "test"} // <- ReactNode
</Custom>
</p>
Rzeczywiście, zwracają różne rzeczy.Dlaczego metody renderowania komponentów klasy zwracają ReactNode, ale komponenty funkcji zwracają Reacttelement?
Component
s return:
render(): ReactNode;
I funkcje są "składnikami bezstanowymi":
interface StatelessComponent<P = {}> {
(props: P & { children?: ReactNode }, context?: any): ReactElement | null;
// ... doesn't matter
}
Jest to spowodowane względami historycznymi.
Jak to rozwiązać w odniesieniu do null?
Wpisz go jako ReactElement | null
tak jak robi to react. Albo niech Typescript wywnioskuje Typ.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-09 06:38:54
1.) Jaka jest różnica między JSX.Element, ReactNode i ReactElement?
ReactElement i JSX.Element
są wynikiem wywołania React.createElement
bezpośrednio lub poprzez transpilację JSX. Jest to obiekt o type
, props
i key
. JSX.Element
jest ReactElement
, którego props
i type
mają typ any
, więc są mniej więcej takie same.
const jsx = <div>hello</div>
const ele = React.createElement("div", null, "hello");
ReactNode jest używany jako typ zwracający render()
W komponentach klasy. Jest to również domyślne type for children
atrybut with PropsWithChildren
.
const Comp: FunctionComponent = props => <div>{props.children}</div>
// children?: React.ReactNode
Wygląda to bardziej skomplikowanie w deklaracjach typu React , ale jest odpowiednikiem do:
type ReactNode = {} | null | undefined;
// super type `{}` has absorbed *all* other types, which are sub types of `{}`
// so it is a very "broad" type (I don't want to say useless...)
Możesz przypisać prawie wszystko do ReactNode
. Zazwyczaj wolałabym mocniejsze typy, ale może być kilka ważnych przypadków, aby go użyć.
2.) Dlaczego metody renderowania komponentów klasy zwracają ReactNode, ale komponenty funkcji zwracają ReactElement?
Tl; dr: Jest to aktualna niezgodność typu TS niezwiązana z reakcją rdzenia.
-
Komponent klasy TS: zwraca
ReactNode
zrender()
, bardziej permisywny niż React / JS -
Komponent funkcji TS: zwraca
JSX.Element | null
, bardziej restrykcyjny niż React / JS
W zasadzie, render()
W komponentach klasy React/JS obsługuje te same typy zwrotne jako komponent funkcyjny. W odniesieniu do TS, różne typy są rodzajem niespójności nadal utrzymywane ze względów historycznych i konieczności wstecznej zgodności.
Najlepiej valid return type prawdopodobnie wyglądałby bardziej tak:
type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number
| boolean | null // Note: undefined is invalid
Niektóre opcje:3.) Jak to rozwiązać w odniesieniu do null?
// Use type inference; inferred return type is `JSX.Element | null`
const MyComp1 = ({ condition }: { condition: boolean }) =>
condition ? <div>Hello</div> : null
// Use explicit function return types; Add `null`, if needed
const MyComp2 = (): JSX.Element => <div>Hello</div>;
const MyComp3 = (): React.ReactElement => <div>Hello</div>;
// Option 3 is equivalent to 2 + we don't need to use a global (JSX namespace)
// Use built-in `FunctionComponent` or `FC` type
const MyComp4: React.FC<MyProps> = () => <div>Hello</div>;
Uwaga: unikanie React.FC
nie zapisuje Ciebie z JSX.Element | null
return type restriction.
Tworzenie Reactowej aplikacji Ostatnio porzucone React.FC
Z jego szablonu, ponieważ ma pewne dziwactwa jak ukryty {children?: ReactNode}
definicja typu. Więc używanie React.FC
oszczędnie może być lepsze.
const MyCompFragment: FunctionComponent = () => <>"Hello"</>
const MyCompCast: FunctionComponent = () => "Hello" as any
// alternative to `as any`: `as unknown as JSX.Element | null`
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55