TypeScript e React - tipo di bambini?


138

Ho un componente funzionale molto semplice come segue:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


const aux = (props: AuxProps) => props.children;

export default aux;

E un altro componente:

import * as React from "react";

export interface LayoutProps  { 
   children: React.ReactNode
}

const layout = (props: LayoutProps) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {props.children}
        </main>
    <Aux/>
);

export default layout;

Continuo a ricevere il seguente errore:

[ts] Il tipo di elemento JSX "ReactNode" non è una funzione di costruzione per elementi JSX. Il tipo "undefined" non è assegnabile al tipo "ElementClass". [2605]

Come lo digito correttamente?

Risposte:


133

Appena children: React.ReactNode


1
Questa è la risposta corretta qui! JSX.Elementnon è abbastanza buono poiché un React children valido potrebbe essere una stringa, un booleano, null ... ReactChildè anche incompleto per le stesse ragioni
Pierre Ferry

2
@ PierreFerry Il problema è che quasi tutto può essere assegnato ReactNode. Non aiuta in termini di sicurezza dei tipi, simile alla digitazione childrencome any- vedi la mia risposta per una spiegazione.
ford04

Grazie per la tua risposta @ ford04. Nel mio caso d'uso, voglio che i bambini siano di tipo approssimativo. Il mio componente accetta qualsiasi tipo di bambino React valido. Quindi credo che questa sia ancora la risposta che stavo cercando :)
Pierre Ferry il

47

Per <Aux>poterlo utilizzare nel tuo JSX, deve essere una funzione che restituisce ReactElement<any> | null. Questa è la definizione di un componente funzione .

Tuttavia, è attualmente definita come una funzione che restituisce React.ReactNode, che è un tipo molto più ampio. Come dicono le digitazioni di React:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

Assicurati che i tipi indesiderati siano neutralizzati avvolgendo il valore restituito in React Fragment ( <></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;

Non capisco perché sia ​​necessario avvolgere childrenin un frammento di reazione. Ti va di spiegare? In React è perfettamente valido solo return children;.
sanfilippopablo

1
Non se childrensono un array. In tal caso, è necessario avvolgerli in un singolo nodo o utilizzare React Fragments.
Karol Majewski

Sì, nel mio codice ho return React.Children.count(children) > 1 ? <>{children}</> : children:, ma il dattiloscritto si lamenta di questo. L'ho sostituito con solo <>{children}</>.
sanfilippopablo

2
Si lamenta perché childrenè un elenco di elementi che contiene solo 1 elemento. Penso che funzionerebbe se lo facessi return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2

Questo non sembra funzionare nelle versioni più recenti di React. Consolo registrato e posso confermare di avere un sostegno per bambini, ma anche con l' <React.Fragment>ambiente {props.children}non restituisco nulla.
Segna il

34

Questo è ciò che ha funzionato per me:

interface Props {
  children: JSX.Element[] | JSX.Element
}

Modifica Consiglierei di usarlo children: React.ReactNodeora.


8
Non è una definizione abbastanza ampia. Il bambino potrebbe essere una stringa.
Mike S

Almeno questo esempio ha funzionato per me poiché il mio componente avrebbe dovuto aspettarsi 1 o più JSX.Elements. Grazie!
Italo Borges

1
Questo non funzionerà con le espressioni JavaScript come figli <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. L'utilizzo children: ReactNodefunzionerà.
Nickofthyme

10

puoi dichiarare il tuo componente in questo modo:

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

9

Puoi usare ReactChildrene ReactChild:

import React, { ReactChildren, ReactChild } from 'react';

interface AuxProps {
  children: ReactChild | ReactChildren;
}

const Aux = ({ children }: AuxProps) => (<div>{children}</div>);

export default Aux;


6

Il tipo restituito del componente funzione è limitato a JSXElement | nullin TypeScript. Questa è una limitazione del tipo corrente, React puro consente più tipi di restituzione.

Snippet dimostrativo minimo

Puoi utilizzare un'asserzione di tipo o Fragments come soluzione alternativa:

const Aux = (props: AuxProps) => <>props.children</>; 
const Aux2 = (props: AuxProps) => props.children as ReactElement; 

ReactNode

children: React.ReactNodepotrebbe non essere ottimale, se l'obiettivo è avere tipi forti per Aux.

Quasi tutto può essere assegnato al ReactNodetipo corrente , che è equivalente a {} | undefined | null. Un tipo più sicuro per il tuo caso potrebbe essere:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Esempio:

Date le Auxesigenze di elementi React come children, abbiamo accidentalmente aggiunto un stringa. Quindi la soluzione sopra sarebbe stata un errore in contrasto con ReactNode: dai un'occhiata ai campi da gioco collegati.

I caratteri tipizzati childrensono utili anche per oggetti di scena non JSX, come un callback di rendering di oggetti .


5

Puoi anche usare React.PropsWithChildren<P>.

type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;

3

Il modo generale per trovare qualsiasi tipo è l'esempio. Il bello del dattiloscritto è che hai accesso a tutti i tipi, purché tu abbia i @types/file corretti .

Per rispondere io stesso ho solo pensato a un componente che reagisce agli usi che ha il childrensostegno. La prima cosa che ti è venuta in mente? Che ne dici di un <div />?

Tutto quello che devi fare è aprire vscode e creare un nuovo .tsxfile in un progetto react con @types/react.

import React from 'react';

export default () => (
  <div children={'test'} />
);

Passare il mouse sopra l' childrenelica ti mostra il tipo. E cosa sai - Il suo tipo è ReactNode(non necessario ReactNode[]).

inserisci qui la descrizione dell'immagine

Quindi, se fai clic sulla definizione del tipo, ti porta direttamente alla definizione di childrenproveniente DOMAttributesdall'interfaccia.

// node_modules/@types/react/index.d.ts
interface DOMAttributes<T> {
  children?: ReactNode;
  ...
}

Nota: questo processo dovrebbe essere utilizzato per trovare qualsiasi tipo sconosciuto! Sono tutti lì che aspettano solo che tu li trovi :)


2

Come tipo che contiene bambini, sto usando:

type ChildrenContainer = Pick<JSX.IntrinsicElements["div"], "children">

Questo tipo di contenitore figlio è abbastanza generico da supportare tutti i diversi casi e anche allineato con l'API ReactJS.

Quindi, per il tuo esempio, sarebbe qualcosa del tipo:

const layout = ({ children }: ChildrenContainer) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {children}
        </main>
    <Aux/>
)

1

Queste risposte sembrano essere obsolete - React ora ha un tipo integrato PropsWithChildren<{}>. È definito in modo simile ad alcune delle risposte corrette in questa pagina:

type PropsWithChildren<P> = P & { children?: ReactNode };


1
Cosa c'è Pnel caso di quel tipo?
sunpietro

Pè il tipo di oggetti di scena del tuo componente
Tim Iles

-2

I componenti React dovrebbero avere un singolo nodo wrapper o restituire un array di nodi.

Il tuo <Aux>...</Aux>componente ha due nodi dive main.

Cercate di avvolgere i vostri figli in un diva Auxcomponente.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

const aux = (props: AuxProps) => (<div>{props.children}</div>);

export default aux;

1
Non vero. In React 16+ questo non è più il caso, inoltre l'errore lo direbbe se fosse il caso
Andrew Li
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.