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?

Asool
la source

Réponses:

133

Juste children: React.ReactNode

Ridd
la source
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}</>;
Karol Majewski
la source
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.

Sunknudsen
la source
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;
}
jsina
la source
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;
Wilk
la source
6

Depuis le site TypeScript: https://github.com/Microsoft/TypeScript/issues/6471

La pratique recommandée est d'écrire le type d'accessoires comme {children ?: any}

Cela a fonctionné pour moi. Le nœud enfant peut être de nombreuses choses différentes, donc un typage explicite peut manquer des cas.

Il y a une discussion plus longue sur le problème de suivi ici: https://github.com/Microsoft/TypeScript/issues/13618 , mais l'approche any fonctionne toujours.

Mike S
la source
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 .

ford04
la source
5

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

type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;
Sibren
la source
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 :)

Nickofthyme
la source
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/>
)
Denis
la source
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 };

Tim Iles
la source
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;
Dinesh Pandiyan
la source
1
Pas vrai. Dans React 16+, ce n'est plus le cas, plus l'erreur dirait ceci si c'était le cas
Andrew Li