Definitelytyped: Les composants React doivent renvoyer ReactNode, ou les enfants doivent être ReactElement

Créé le 14 juil. 2017  ·  18Commentaires  ·  Source: DefinitelyTyped/DefinitelyTyped

  • [x] J'ai essayé d'utiliser le package @types/react et j'ai eu des problèmes.
  • [x] J'ai essayé d'utiliser la dernière version stable de tsc. https://www.npmjs.com/package/typescript
  • [x] J'ai une question inappropriée pour StackOverflow . (Veuillez y poser toutes les questions appropriées).
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) les auteurs (voir Definitions by: dans index.d.ts ) afin qu'ils puissent répondre.

    • Auteurs : @ericanderson @onigoetz @tkrotoff @digiguru @morcerf @johnnyreilly @bbenezech @pzavolinsky @DovydasNavickas

Les composants suivants

const SFC: React.StatelessComponent = (props) => props.children;

class C extends React.Component {
  render() {
    return this.props.children;
  }
}

donner les erreurs

Type '(props: { children?: ReactNode; }) => ReactNode' is not assignable to type 'StatelessComponent<{}>'.
  Type 'ReactNode' is not assignable to type 'ReactElement<any> | null'.
    Type 'undefined' is not assignable to type 'ReactElement<any> | null'.

Class 'C' incorrectly extends base class 'Component<{}, {}>'.
  Types of property 'render' are incompatible.
    Type '() => ReactNode' is not assignable to type '() => false | Element | null'.
      Type 'ReactNode' is not assignable to type 'false | Element | null'.
        Type 'undefined' is not assignable to type 'false | Element | null'.

Cela fonctionne si vous enveloppez children dans un Element .

Retourner children est assez courant lors de la création de composants Provider qui ajoutent simplement quelque chose à context .

Commentaire le plus utile

J'emballe actuellement children dans React.Fragment pour contourner ce problème.

Tous les 18 commentaires

J'ai essayé les correctifs suivants pour index.d.ts

-        render(): JSX.Element | null | false;
+        render(): ReactNode;

-        (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
+        (props: P & { children?: ReactNode }, context?: any): ReactNode;

mais j'ai eu des erreurs partout en essayant de pelucher que je ne sais pas comment corriger.

TypeScript compile error: JSX element type 'ReactNode' is not a constructor function for JSX elements.

Non, ce changement est incorrect. Vous ne pouvez pas retourner plusieurs éléments dans React actuellement. Même si vous le faites quelque part, il est fort probable qu'il soit encapsulé dans un seul d'une manière ou d'une autre.
Seulement dans React 16 avec Fiber cela pourrait être possible :)

je ne sais pas s'ils ont changé le comportement de réaction, mais l'auteur de redux / react a montré la possibilité de renvoyer this.props.children pour créer des wrappers minces (uniquement pour ajouter childContext aux éléments), comme on le voit dans https://egghead.io/lessons/ javascript-redux-passing-the-store-down-implicitly-via-context

react-native vous permet cependant de renvoyer un tableau d'éléments JSX à partir du rendu. une solution consiste à envelopper le this.props.children avec React.Children.only(this.props.children) et il lancera s'il y a plus d'un élément en tant que racine

À droite, vous pouvez utiliser React.Children.only mais ReactNode ET ReactElement sont des valeurs valides de this.props.children .

const Title = props => <h1>{props.children}</h1>;
<Title>Woah</Title>

est un code React valide et raisonnable.

Vous pouvez renvoyer un tableau d'éléments dans React 16, mais TS génère une erreur car il pense que vous ne pouvez pas.

Edit : faites ceci children: JSX.Element[]; pour le réparer

D'après mon expérience jusqu'à présent, il semble que ReactNode devrait être valide.

const Id: React.SFC<{}> = (props) => props.children

const App = ()= > <Id>This should work but instead fails</Id>

Y a-t-il une chance que cela se reflète dans @types/react ?

Sans cela, je suis incapable de taper React children

J'emballe actuellement children dans React.Fragment pour contourner ce problème.

Des mises à jour à ce sujet ?

Je ne sais pas si c'est plus ou moins correct que d'envelopper children dans un React.Fragment , mais j'ai eu de la chance de le faire

return (children as React.ReactElement);

return <>{foo(}</>; toute la nuit. <> est une solution de contournement.

Cela me cause aussi des problèmes.

TL ; RD :

J'ai un HOC qui ajoute des accessoires à un composant donné, et j'essaie de l'utiliser avec un composant fonctionnel qui a return children; . Cela ne fonctionne que si j'utilise la solution de contournement return <>{children}</>; .

Plus de détails:

Voici une version simplifiée du HOC :

export interface ExtraProps {
  extraProp1: string;
}

export const withExtraProps = <P extends object>(Component: React.ComponentType<P>) => {
  return class extends React.Component<Omit<P, keyof ExtraProps>> {
    render() {
      const extraProps = {
        extraProp1: 'test'
      };

      return (
        <Component {...this.props as P} {...extraProps} />
      )
    }
  }
}

Voici une version simplifiée du composant fonctionnel :

interface ComponentProps extends ExtraProps {
  children?: React.ReactNode;
  loadingComponent?: ReactElement;
}

export function ExampleComponent(props: ComponentProps) {
  const { children, loadingComponent } = props;

  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      await someAsyncCall();
      setLoading(false);
    }

    fetchData();
  });

  if (loading) {
    return loadingComponent || null;
  }

  return children;
}

// This line errors:
export const FeatureFlag = withExtraProps(ExampleComponent);

J'obtiens l'erreur suivante :

Argument of type '(props: ComponentProps) => {} | null | undefined' is not assignable to parameter of type 'ComponentType<ComponentProps>'.
  Type '(props: ComponentProps) => {} | null | undefined' is not assignable to type 'FunctionComponent<ComponentProps>'.
    Type '{} | null | undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.
      Type 'undefined' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.

Si j'ajoute un type de retour de ReactElement | null au ExampleComponent, il arrête de se plaindre de le transmettre au HOC, mais toujours des erreurs où je renvoie les enfants :

export function ExampleComponent(props: ComponentProps): ReactElement | null {
  ...
  ...
  return children; // Type 'ReactNode' is not assignable to type 'ReactElement<any, string | ((props: any) => ReactElement<any, string | ... | (new (props: any) => Component<any, any, any>)> | null) | (new (props: any) => Component<any, any, any>)> | null'.

}

Comme pour les autres ici, si je le change en return <>{children}</>; , cela fonctionne, mais cela semble grossier et inutile.

J'ai _exact_ le même problème que @kyrstenkelly - Une fonction HOC est passée à un React.FC qui rend ses enfants.

Le correctif <> children(...) </> fonctionne, mais existe-t-il une meilleure solution ?

J'ai aussi ce problème ! Je suis en train de suggérer une solution de solution d'emballage avec React.Fragment

Pareil ici

interface IIntlMessageProps {
    id: string;
    valuesGiven?: {[key:string]:string};
}

// this doesn't work - -- TS2769 Type 'null' is not assignable to type '(nodes:ReactNodeARray) => ReactNode' | ... | undefined
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            {props.children}
        </FormattedMessage>
    )
};
//Nope -- TS2769 Type 'Element' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            <React.Fragment>{props.children}</React.Fragment>
        </FormattedMessage>
    )
};

// Nope -- TS2769 Type 'Element' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> | null = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            <>{props.children}</>
        </FormattedMessage>
    )
};

// Nope -- TS2769 Type '{}' is not assignable to type '(nodes:ReactNodeARray) => ReactNode'
export const IntlMessage: React.FC<IIntlMessageProps> = (props) => {
    return (
        <FormattedMessage id={props.id} values={props.valuesGiven}>
            {props.children ? props.children : undefined}
        </FormattedMessage>
    )
};


Je crois que j'ai essayé tout ce qui a été conseillé ici... :( Un indice ?

La solution est que vous devez l'envelopper en fragment.

import React from 'react'

const Something: React.FC = ({ children }) => (
 <> { children } </>
)

cela corrigera l'erreur Type '({ children }: { children?: ReactNode; }) => ReactNode' is not assignable to type 'FunctionComponent .

Si possible, j'aimerais éviter les fragments et faire en sorte que le système de type JSX prenne en charge les mêmes types de constructions que JavaScript et Flow React prennent en charge, ce qui aide TypeScript à mieux fonctionner avec les modèles de bibliothèque tiers.

Un autre non-idéal que je trouve est que les fragments approfondissent l'arborescence des composants, en particulier dans les composants utilitaires/wrapper (cas d'espèce : react-placeholder). Caster vers un React.ReactElement semble être une meilleure approche dans ce type de bibliothèques, mais je préfère ne pas avoir besoin de lancer.

J'ai essayé une approche pour changer ce type, mais je suis resté bloqué avec la vérification de type JSX en utilisant un composant fonctionnel du nouveau type. J'ai toujours la branche, j'aurais juré avoir fait un PR demandant de l'aide, je ne la retrouve pas...

D'accord, si le type doit interdire les tableaux de nœuds, cela ne signifie pas qu'il doit interdire ReactText , ReactPortal , boolean , null et undefined ainsi que l'utilisation de ReactElement .

Quel est le rationnel d'interdire ces types supplémentaires?

Cette page vous a été utile?
0 / 5 - 0 notes