reactjs - Hur konverterar man `props.children` till ett JSX-element?


Med React-funktionskomponenter kan vi rendera barn så här:

import React from 'react';

const MyComponent = (props: React.PropsWithChildren) => {
    return props.children;
}

Men när jag försöker använda detta får jag ett fel:

...
    return <MyComponent />;
...

eller

...
    return (
        <MyComponent>
            <div>hello</div>
        </MyComponent>
    );
...

leder till

'MyComponent' cannot be used as a JSX component. Its return type 'ReactNode' is not a valid JSX element. Type 'undefined' is not assignable to type 'Element | null'.

En lösning är att omsluta den funktionella komponentens returvärde i en fragment:

const MyComponent = (props: React.PropsWithChildren) => {
    return <>{props.children}</>; // <= useless fragment is here
}

Men då triggar jag eslint: react/jsx-no-useless-fragment-felet.

Finns det ett sätt att använda barnen så att jag inte har något av dessa problem?

Redigera

Här är en lekplatslänk

Finns det ett sätt att konvertera från en ReactNode till en giltig JSX-komponent annat än genom att använda ett fragment?

Ytterligare anteckning: eslint-plugin-react har ett alternativ för allowExpressions som täcker detta användningsfall. I princip tillåter det "meningslösa" fragment när fragmentet utför jobbet med ReactNode => JSX-elementkonvertering.

Svar #1

Den "värdelösa" fragmentet är faktiskt samma som att kalla

React.createElement(React.Fragment, null, props.children);

React.createElement kan använda en komponenttyp (React.Fragment) och barn för att skapa ett JSX-element av den typen med de barnen.

Så lösningen är att göra så här:

const MyComponent = (props: React.PropsWithChildren) => {
    return React.createElement(React.Fragment, null, props.children);
}

Det gör både TS och eslint glada. Dock föredras JSX framför createElement. Att aktivera alternativet {"allowExpressions": true} för eslint-regeln (se fråga för mer detaljer) och använda ett JSX-stil fragment är det bästa valet här.

Tack till alla för hjälpen.

Svar #2

Det finns 2 viktiga punkter jag ser här:

  1. Den revision du gav i din senaste kodblock med barnen inslutna i ett fragment är viktig i Typescript. Barn kan vara en array av element vilket inte skulle vara ett giltigt element, så det behöver omslutas i ett giltigt element för att täcka det fallet (eller kontrolleras för att säkerställa att det inte är en array). (Åtminstone i Typescript och React före v16.)

  2. Den eslint-regel du refererar till som orsakar det andra felet utlöses eftersom det bara finns ett objekt inuti fragmentet. Men eftersom det är det vi vill för att passa Typescript-definitionen, är det problematiskt. Jag skulle rekommendera att tysta det felet i just det här fallet.

Ett alternativ är att lägga till en inaktiveringskommentar på raden före return som detta:

    // eslint-disable-next-line react/jsx-no-useless-fragment
    return <>{props.children}</>;

Relaterade artiklar