TypeScript et React - type enfants?


138

J'ai un composant fonctionnel très simple comme suit:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

Et un autre composant:

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;

Je continue à recevoir l'erreur suivante:

[ts] Le type d'élément JSX 'ReactNode' n'est pas une fonction de constructeur pour les éléments JSX. Le type 'undefined' n'est pas attribuable au type 'ElementClass'. [2605]

Comment puis-je taper ceci correctement?

Réponses:


133

Juste children: React.ReactNode


1
C'est la bonne réponse ici! JSX.Elementn'est pas assez bon car un enfant React valide pourrait être une chaîne, un booléen, null ... ReactChildest incomplet aussi pour les mêmes raisons
Pierre Ferry

2
@PierreFerry Le problème est que presque tout peut être attribué ReactNode. Cela n'aide pas en termes de sécurité de type, similaire à la saisie childrencomme any- voir ma réponse pour une explication.
ford04

Merci pour votre réponse @ ford04. Dans mon cas d'utilisation, je veux que les enfants soient de type lâche. Mon composant accepte tout type d'enfants React valides. Donc je crois que c'est toujours la réponse que je cherchais :)
Pierre Ferry

47

Pour pouvoir l'utiliser <Aux>dans votre JSX, il doit s'agir d'une fonction qui renvoie ReactElement<any> | null. C'est la définition d'un composant de fonction .

Cependant, il est actuellement défini comme une fonction qui renvoie React.ReactNode, qui est un type beaucoup plus large. Comme le disent les typages React:

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

Assurez-vous que les types indésirables sont neutralisés en enveloppant la valeur retournée dans React Fragment ( <></>):

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

Je ne comprends pas pourquoi il est nécessaire d'envelopper childrenun fragment de réaction. Soin d'expliquer? Dans React, c'est parfaitement valable pour juste return children;.
sanfilippopablo

1
Pas si childrensont un tableau. Si tel est le cas, vous devez les envelopper dans un seul nœud ou utiliser React Fragments.
Karol Majewski

Ouais, dans mon code, j'ai return React.Children.count(children) > 1 ? <>{children}</> : children:, mais dactylographié s'en plaint. Je l'ai remplacé par juste <>{children}</>.
sanfilippopablo

2
Il se plaint parce que childrenc'est une liste d'éléments qui ne contient qu'un seul élément. Je pense que cela fonctionnerait si vous return React.Children.count(children) > 1 ? <>{children}</> : children[0]faisiez @sanfilippopablo
tamj0rd2

Cela ne semble pas fonctionner dans les nouvelles versions de React. Je suis connecté à la console et je peux confirmer que j'ai un accessoire pour enfants, mais même avec les <React.Fragment>environs, {props.children}je ne retourne rien.
Mark le

34

C'est ce qui a fonctionné pour moi:

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

Modifier Je recommanderais d'utiliser à la children: React.ReactNodeplace maintenant.


8
Ce n'est pas une définition assez large. L'enfant pourrait être une chaîne.
Mike S

Au moins, cet exemple a fonctionné pour moi puisque mon composant était censé attendre 1 ou plusieurs éléments JSX.Elements. Merci!
Italo Borges

1
Cela ne fonctionnera pas avec les expressions JavaScript comme enfants <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. L'utilisation children: ReactNodefonctionnera.
Nickofthyme

10

vous pouvez déclarer votre composant comme ceci:

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

9

Vous pouvez utiliser ReactChildrenet ReactChild:

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

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;


6

Le type de retour du composant de fonction est limité à JSXElement | nulldans TypeScript. Il s'agit d'une limitation de type actuelle, pure React permet plus de types de retour.

Extrait de démonstration minimal

Vous pouvez utiliser une assertion de type ou des fragments comme solution de contournement:

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

ReactNode

children: React.ReactNodepeut être sous-optimal, si l'objectif est d'avoir des types forts pour Aux.

Presque tout peut être attribué au ReactNodetype actuel , ce qui équivaut à {} | undefined | null. Un type plus sûr pour votre cas pourrait être:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Exemple:

Compte tenu des Auxbesoins des éléments React children, nous y avons accidentellement ajouté un string. Ensuite, la solution ci-dessus serait une erreur par rapport à ReactNode- jetez un œil aux terrains de jeux liés.

Les caractères typés childrensont également utiles pour les accessoires non JSX, comme un rappel Render Prop .


5

Vous pouvez également utiliser React.PropsWithChildren<P>.

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

3

La manière générale de trouver n'importe quel type est par exemple. La beauté de la dactylographie est que vous avez accès à tous les types, tant que vous avez les bons @types/fichiers.

Pour y répondre moi-même, je viens de penser à un composant qui utilise un childrenaccessoire. La première chose qui vous est venue à l'esprit? Et un <div />?

Tout ce que vous avez à faire est d'ouvrir vscode et de créer un nouveau .tsxfichier dans un projet react avec @types/react.

import React from 'react';

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

Le survol de l' childrenaccessoire vous montre le type. Et que savez-vous - Son type est ReactNode(pas besoin de ReactNode[]).

entrez la description de l'image ici

Ensuite, si vous cliquez dans la définition de type, cela vous amène directement à la définition de la childrenprovenance de l' DOMAttributesinterface.

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

Remarque: ce processus doit être utilisé pour trouver tout type inconnu! Tous sont là en attendant que vous les trouviez :)


2

En tant que type contenant des enfants, j'utilise:

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

Ce type de conteneur enfants est suffisamment générique pour prendre en charge tous les différents cas et également aligné avec l'API ReactJS.

Donc, pour votre exemple, ce serait quelque chose comme:

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

1

Ces réponses semblent obsolètes - React a maintenant un type intégré PropsWithChildren<{}>. Il est défini de la même manière que certaines des bonnes réponses sur cette page:

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


1
Qu'en est P-il de ce type?
sunpietro

Pest le type d'accessoires de votre composant
Tim Iles

-2

Les composants React doivent avoir un seul nœud wrapper ou renvoyer un tableau de nœuds.

Votre <Aux>...</Aux>composant a deux nœuds divet main.

Essayez d'envelopper vos enfants dans un composant divin Aux.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;

1
Pas vrai. Dans React 16+, ce n'est plus le cas, plus l'erreur dirait ceci si c'était le cas
Andrew Li
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.