Definitelytyped: Los componentes de React deben devolver ReactNode, o los niños deben ser ReactElement

Creado en 14 jul. 2017  ·  18Comentarios  ·  Fuente: DefinitelyTyped/DefinitelyTyped

  • [x] Intenté usar el paquete @types/react y tuve problemas.
  • [x] Intenté usar la última versión estable de tsc. https://www.npmjs.com/package/mecanografiado
  • [x] Tengo una pregunta que no es apropiada para StackOverflow . (Por favor haga las preguntas apropiadas allí).
  • [x] [Mencionar](https://github.com/blog/821-mention-somebody-they-re-notified) a los autores (ver Definitions by: en index.d.ts ) para que puedan responder.

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

Los siguientes componentes

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

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

dar los errores

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'.

Funciona si envuelves children en un Element .

Devolver children es bastante común cuando se crean componentes de proveedor que solo agregan algo a context .

Comentario más útil

Actualmente estoy envolviendo children en React.Fragment para evitar esto.

Todos 18 comentarios

Probé las siguientes correcciones a 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;

pero tengo errores por todas partes cuando intento quitar la pelusa que no estoy seguro de cómo solucionar.

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

No, este cambio es incorrecto. No puede devolver múltiples elementos en React actualmente. Incluso si lo hace en algún lugar, lo más probable es que esté encapsulado en uno solo de alguna manera.
Solo en React 16 con Fiber esto podría ser posible :)

no estoy seguro de si cambiaron el comportamiento de reacción, pero el autor de redux / react mostró la capacidad de devolver this.props.children para crear envolturas delgadas (solo para agregar childContext a los elementos), como se ve en https://egghead.io/lessons/ javascript-redux-passing-the-store-down-implícitamente-a través de contexto

Sin embargo, react-native le permite devolver una matriz de elementos JSX desde el renderizado. una solución es envolver el this.props.children con React.Children.only(this.props.children) y arrojará si hay más de un solo elemento como raíz

Correcto, puede usar React.Children.only pero tanto ReactNode Y ReactElement son valores válidos de this.props.children .

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

es un código React válido y razonable.

Puede devolver una matriz de elementos en la reacción 16, pero TS arroja un error porque cree que no puede.

Editar: haz esto children: JSX.Element[]; para arreglarlo

Según mi experiencia hasta ahora, parece que ReactNode debería ser válido.

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

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

¿Hay alguna posibilidad de que esto se refleje en @types/react ?

Sin esto, no puedo escribir React children.

Actualmente estoy envolviendo children en React.Fragment para evitar esto.

¿Alguna actualización al respecto?

No estoy seguro de si esto es más o menos correcto que envolver children en un React.Fragment , pero he tenido suerte al hacerlo

return (children as React.ReactElement);

return <>{foo(}</>; toda la noche. <> es una solución alternativa.

Esto también me está causando problemas.

TL;RD:

Tengo un HOC que agrega accesorios a un componente dado y estoy tratando de usarlo con un componente funcional que tiene return children; . Solo funciona si uso la solución alternativa return <>{children}</>; .

Más detalles:

Aquí hay una versión simplificada del 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} />
      )
    }
  }
}

Aquí hay una versión simplificada del componente funcional:

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);

Obtuve el siguiente error:

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 agrego un tipo de retorno de ReactElement | null al ExampleComponent, deja de quejarse de pasarlo al HOC, pero sigue teniendo errores cuando devuelvo a los elementos secundarios:

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'.

}

Al igual que con los otros aquí, si lo cambio a return <>{children}</>; , funciona, pero se siente asqueroso e innecesario.

Tengo el mismo problema _exacto_ que @kyrstenkelly : se pasa una función HOC a React.FC que representa a sus hijos.

La solución <> children(...) </> funciona, pero ¿hay una solución mejor todavía?

¡También tengo este problema! Actualmente estoy haciendo la solución sugerida de la solución de envoltura con React.Fragment

Aquí igual

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>
    )
};


Creo que he probado todo lo que se aconseja aquí... :( ¿Alguna pista?

La solución es que necesitas envolverlo en un fragmento.

import React from 'react'

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

esto solucionará el error Type '({ children }: { children?: ReactNode; }) => ReactNode' is not assignable to type 'FunctionComponent .

Si es posible, me gustaría evitar los fragmentos y hacer que el sistema de tipos JSX admita los mismos tipos de construcciones que admiten JavaScript y Flow React, lo que ayuda a que TypeScript funcione mejor con patrones de biblioteca de terceros.

Otro no ideal que encuentro es que los fragmentos profundizan el árbol de componentes, especialmente en los componentes de utilidad/envoltura (caso en cuestión: marcador de posición de reacción). Enviar a React.ReactElement parece un mejor enfoque en este tipo de bibliotecas, pero prefiero no tener que enviar.

Intenté un enfoque para cambiar ese tipo, pero me quedé atascado con la verificación de tipos JSX usando un componente funcional del nuevo tipo. Todavía tengo la sucursal, podría haber jurado que hice un PR pidiendo ayuda, no puedo volver a encontrarla...

De acuerdo, si type necesita no permitir matrices de nodos, no significa que deba no permitir ReactText , ReactPortal , boolean , null y undefined también lo que hace el uso de ReactElement .

¿Cuál es la razón de prohibir estos tipos adicionales?

¿Fue útil esta página
0 / 5 - 0 calificaciones