Definitelytyped: Os componentes React devem retornar ReactNode, ou os filhos devem ser ReactElement

Criado em 14 jul. 2017  ·  18Comentários  ·  Fonte: DefinitelyTyped/DefinitelyTyped

  • [x] Tentei usar o pacote @types/react e tive problemas.
  • [x] Tentei usar a versão estável mais recente do tsc. https://www.npmjs.com/package/typescript
  • [x] Tenho uma pergunta inapropriada para StackOverflow . (Por favor, faça todas as perguntas apropriadas lá).
  • [x] [Mencione](https://github.com/blog/821-mention-somebody-they-re-notified) os autores (veja Definitions by: em index.d.ts ) para que eles possam responder.

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

Os seguintes componentes

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

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

dê os erros

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 se você envolver children em um Element .

Retornar children é bastante comum ao fazer componentes Provider que apenas adicionam algo a context .

Comentários muito úteis

Atualmente estou envolvendo children em React.Fragment para contornar isso.

Todos 18 comentários

Eu tentei as seguintes correções para 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;

mas recebi erros em todo o lugar ao tentar lint que não tenho certeza de como corrigir.

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

Não, esta alteração está incorreta. Você não pode retornar vários elementos no React atualmente. Mesmo se você fizer isso em algum lugar, provavelmente está encapsulado em um único de alguma forma.
Apenas no React 16 com Fiber isso pode ser possível :)

não tenho certeza se eles mudaram o comportamento de reação, mas o autor redux/react mostrou a capacidade de retornar this.props.children para criar wrappers finos (apenas para adicionar childContext a elementos), como visto em https://egghead.io/lessons/ javascript-redux-passing-the-store-down-implicitly-via-context

O react-native permite que você retorne uma matriz de elementos JSX da renderização. uma solução é envolver o this.props.children com React.Children.only(this.props.children) e ele será lançado se houver mais de um único elemento como raiz

Certo, você pode usar React.Children.only mas ambos ReactNode E ReactElement são valores válidos de this.props.children .

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

é um código React válido e razoável.

Você pode retornar uma matriz de elementos em react 16, mas o TS lança um erro porque acha que você não pode.

Edit: faça isso children: JSX.Element[]; para corrigi-lo

Da minha experiência até agora, parece que o ReactNode deve ser válido.

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

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

Existe alguma chance de isso se refletir em @types/react ?

Sem isso, não consigo digitar React children

Atualmente estou envolvendo children em React.Fragment para contornar isso.

Alguma atualização sobre ele?

Não tenho certeza se isso é mais ou menos correto do que envolver children em um React.Fragment , mas tive sorte em fazer

return (children as React.ReactElement);

return <>{foo(}</>; noite toda. <> é uma solução alternativa.

Isso está causando problemas para mim também.

TL;DR:

Eu tenho um HOC que adiciona props a um determinado componente e estou tentando usá-lo com um componente funcional que possui return children; . Só funciona se eu usar a solução alternativa return <>{children}</>; .

Mais detalhes:

Aqui está uma versão simplificada do 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} />
      )
    }
  }
}

Aqui está uma versão simplificada do 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);

Estou tendo o erro a seguir:

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

Se eu adicionar um tipo de retorno de ReactElement | null ao ExampleComponent, ele para de reclamar de passá-lo para o HOC, mas ainda apresenta erros onde estou retornando os filhos:

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

}

Tal como acontece com os outros aqui, se eu mudar para return <>{children}</>; , funciona, mas parece nojento e desnecessário.

Eu tenho o mesmo problema _exato_ que @kyrstenkelly - Uma função HOC sendo passada React.FC que renderiza seus filhos.

A correção <> children(...) </> funciona, mas existe uma solução melhor ainda?

Também to com esse problema! Atualmente fazendo a sugestão de solução de encapsulamento com React.Fragment

Mesmo aqui

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


Acredito que tentei tudo o que foi aconselhado aqui... :( Alguma pista?

A solução é que você precisa envolvê-lo em fragmento.

import React from 'react'

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

isso corrigirá o erro Type '({ children }: { children?: ReactNode; }) => ReactNode' is not assignable to type 'FunctionComponent .

Se possível, gostaria de evitar fragmentos e fazer com que o sistema de tipos JSX suporte os mesmos tipos de construções que JavaScript e Flow React suportam, o que ajuda o TypeScript a funcionar melhor com padrões de biblioteca de terceiros.

Outro não ideal que acho é que os fragmentos aprofundem a árvore de componentes, especialmente em componentes utilitários/empacotadores (caso em questão: react-placeholder). Casting para um React.ReactElement parece ser uma abordagem melhor neste tipo de biblioteca, mas eu prefiro não precisar lançar.

Eu tentei uma abordagem para alterar esse tipo, mas fiquei preso com a verificação de tipos JSX usando um componente funcional do novo tipo. Ainda tenho a filial, poderia jurar que fiz um PR pedindo ajuda, não consigo encontrá-la de volta...

Ok, se o tipo precisar proibir matrizes de nós, isso não significa que ele deve proibir ReactText , ReactPortal , boolean , null e undefined também o que o uso de ReactElement faz.

Qual o racional de proibir esses tipos adicionais?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

demisx picture demisx  ·  3Comentários

Loghorn picture Loghorn  ·  3Comentários

alisabzevari picture alisabzevari  ·  3Comentários

jbreckmckye picture jbreckmckye  ·  3Comentários

fasatrix picture fasatrix  ·  3Comentários