Ich migriere derzeit eine React-Anwendung zu TypeScript. Bisher funktioniert das ziemlich gut, aber ich habe ein Problem mit den Rückgabetypen meiner render
Funktionen bzw. meiner Funktionskomponenten.
Bisher hatte ich immer JSX.Element
als Rückgabetyp verwendet, jetzt funktioniert dies nicht mehr, wenn eine Komponente beschließt, nichts zu rendern, dh zurückzugeben null
, da dies null
kein gültiger Wert für ist JSX.Element
. Dies war der Beginn meiner Reise, denn jetzt habe ich im Internet gesucht und festgestellt, dass Sie ReactNode
stattdessen verwenden sollten, was auch null
einige andere Dinge beinhaltet, die passieren können. Dies schien die bessere Wahl zu sein.
Beim Erstellen einer Funktionskomponente beschwert sich TypeScript jedoch über den ReactNode
Typ. Nach einigem Suchen stellte ich wieder fest, dass Sie ReactElement
stattdessen für Funktionskomponenten verwenden sollten . Wenn ich dies tue, ist das Kompatibilitätsproblem jedoch behoben, aber jetzt beschwert sich TypeScript erneut darüber, dass es sich null
nicht um einen gültigen Wert handelt.
Um es kurz zu machen, ich habe drei Fragen:
- Was ist der Unterschied zwischen
JSX.Element
,ReactNode
undReactElement
? - Warum geben die
render
Methoden von Klassenkomponenten zurückReactNode
, aber FunktionskomponentenReactElement
? - Wie löse ich das in Bezug auf
null
?
quelle
class Example extends Component<ExampleProps> {
für Klassen undconst Example: FunctionComponent<ExampleProps> = (props) => {
für Funktionskomponenten (woExampleProps
ist eine Schnittstelle für die erwarteten Requisiten). Und dann haben diese Typen genügend Informationen, um auf den Rückgabetyp schließen zu können.Antworten:
Ein ReactElement ist ein Objekt mit einem Typ und Requisiten.
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> { type: T; props: P; key: Key | null; }
Ein ReactNode ist ein ReactElement, ein ReactFragment, eine Zeichenfolge, eine Zahl oder ein Array von ReactNodes oder null oder undefiniert oder ein Boolescher Wert:
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 ist ein ReactElement, wobei der generische Typ für Requisiten und Typ beliebig ist. Es existiert, da verschiedene Bibliotheken JSX auf ihre eigene Weise implementieren können. Daher ist JSX ein globaler Namespace, der dann von der Bibliothek festgelegt wird. React legt es folgendermaßen fest:
declare global { namespace JSX { interface Element extends React.ReactElement<any, any> { } } }
Zum Beispiel:
<p> // <- ReactElement = JSX.Element <Custom> // <- ReactElement = JSX.Element {true && "test"} // <- ReactNode </Custom> </p>
In der Tat geben sie verschiedene Dinge zurück.
Component
s Rückkehr:Und Funktionen sind "zustandslose Komponenten":
interface StatelessComponent<P = {}> { (props: P & { children?: ReactNode }, context?: any): ReactElement | null; // ... doesn't matter }
Dies ist eigentlich aus historischen Gründen .
Geben Sie es
ReactElement | null
genauso ein wie reagieren. Oder lassen Sie Typescript auf den Typ schließen.Quelle für die Typen
quelle
ReactComponent
sollteReact.Component
oder seinComponent
.ReactElement und JSX.Element sind das Ergebnis eines
React.createElement
direkten Aufrufs oder einer JSX-Transpilation. Es ist eine Aufgabe mittype
,props
undkey
.JSX.Element
istReactElement
, wessenprops
undtype
Typ habenany
, also sind sie mehr oder weniger gleich.const jsx = <div>hello</div> const ele = React.createElement("div", null, "hello");
ReactNode wird als Rückgabetyp für
render()
Komponenten in Klassen verwendet. Dies ist auch der Standardtyp fürchildren
Attribute mitPropsWithChildren
.const Comp: FunctionComponent = props => <div>{props.children}</div> // children?: React.ReactNode
In den React-Typdeklarationen sieht es komplizierter aus , entspricht aber:
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...)
Sie können fast alles zuweisen
ReactNode
. Normalerweise würde ich stärkere Typen bevorzugen, aber es könnte einige gültige Fälle geben, um sie zu verwenden.tl; dr: Es handelt sich um eine aktuelle Inkompatibilität vom Typ TS, die nicht mit der Kernreaktion zusammenhängt .
TS-Klassenkomponente: Rückgabe
ReactNode
mitrender()
, zulässiger als React / JSTS-Funktionskomponente: Rückgabe
JSX.Element | null
, restriktiver als React / JSIm Prinzip unterstützen
render()
Komponenten der React / JS-Klasse dieselben Rückgabetypen wie eine Funktionskomponente. In Bezug auf TS sind die verschiedenen Typen eine Typinkonsistenz, die aus historischen Gründen und der Notwendigkeit der Abwärtskompatibilität immer noch beibehalten wird.Im Idealfall würde ein gültiger Rückgabetyp wahrscheinlich eher so aussehen:
type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number | boolean | null // Note: undefined is invalid
Einige Optionen:
// 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>;
Hinweis: Durch das Vermeiden
React.FC
werden Sie nicht vor derJSX.Element | null
Einschränkung des Rückgabetyps bewahrt.Erstellen Reagieren App vor kurzem fiel
In Randfällen können Sie als Problemumgehung eine Typzusicherung oder Fragmente hinzufügen:React.FC
von seiner Vorlage, wie es einige Macken wie eine implizite hat{children?: ReactNode}
Typdefinition. EineReact.FC
sparsame Verwendung ist daher möglicherweise vorzuziehen.const MyCompFragment: FunctionComponent = () => <>"Hello"</> const MyCompCast: FunctionComponent = () => "Hello" as any // alternative to `as any`: `as unknown as JSX.Element | null`
quelle