Definitelytyped: Компоненты React должны возвращать ReactNode или дочерние элементы должны быть ReactElement.

Созданный на 14 июл. 2017  ·  18Комментарии  ·  Источник: DefinitelyTyped/DefinitelyTyped

  • [x] Я пытался использовать пакет @types/react , и у меня возникли проблемы.
  • [x] Я пытался использовать последнюю стабильную версию tsc. https://www.npmjs.com/package/typescript
  • [x] У меня есть вопрос, который не подходит для StackOverflow . (Пожалуйста, задавайте любые соответствующие вопросы там).
  • [x] [Упомяните] (https://github.com/blog/821-mention-somebody-they-re-notified) авторов (см. Definitions by: в index.d.ts ), чтобы они могли отвечать.

    • Авторы: @ericanderson @onigoetz @tkrotoff @digiguru @morcerf @johnnyreilly @bbenezech @pzavolinsky @DovydasNavickas

Следующие компоненты

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

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

дай ошибки

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

Это работает, если вы оберните children в Element .

Возврат children довольно распространен при создании компонентов Provider, которые просто добавляют что-то к context .

Самый полезный комментарий

В настоящее время я оборачиваю children в React.Fragment , чтобы обойти это.

Все 18 Комментарий

Я попробовал следующие исправления для 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;

но у меня повсюду были ошибки при попытке lint, которые я не знаю, как исправить.

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

Нет, это изменение неверно. В настоящее время вы не можете возвращать несколько элементов в React. Даже если вы делаете это где-то, скорее всего, это как-то инкапсулировано в одном.
Только в React 16 с Fiber это возможно :)

не уверен, изменили ли они поведение реакции, но автор редукции/реакции продемонстрировал возможность вернуть this.props.children для создания тонких оберток (только для добавления childContext к элементам), как показано в https://egghead.io/lessons/ javascript-redux-передача-хранилища-вниз-неявно-через-контекст

Однако react-native позволяет вам возвращать массив элементов JSX из рендеринга. обходной путь заключается в том, чтобы обернуть this.props.children в React.Children.only(this.props.children) , и он будет сгенерирован, если в качестве корня имеется более одного элемента.

Правильно, вы можете использовать React.Children.only , но и ReactNode , и ReactElement являются допустимыми значениями this.props.children .

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

является действительным и разумным кодом React.

Вы можете вернуть массив элементов в реакции 16, но TS выдает ошибку, потому что думает, что вы не можете.

Изменить: сделайте это children: JSX.Element[]; , чтобы исправить это

Судя по моему опыту, ReactNode должен работать.

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

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

Есть ли шанс, что это отразится на @types/react ?

Без этого я не могу набирать детей React

В настоящее время я оборачиваю children в React.Fragment , чтобы обойти это.

Есть какие-нибудь обновления на нем?

Не уверен, что это более или менее правильно, чем обертывание children в React.Fragment , но мне повезло с этим

return (children as React.ReactElement);

return <>{foo(}</>; всю ночь напролет. <> — обходной путь.

Это вызывает проблемы и у меня.

TL;DR:

У меня есть HOC, который добавляет свойства к данному компоненту, и я пытаюсь использовать его с функциональным компонентом, имеющим return children; . Это работает, только если я использую обходной путь return <>{children}</>; .

Подробнее:

Вот упрощенная версия 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} />
      )
    }
  }
}

Вот упрощенная версия функционального компонента:

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

Я получаю следующую ошибку:

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

Если я добавлю возвращаемый тип ReactElement | null в ExampleComponent, он перестанет жаловаться на передачу его в HOC, но по-прежнему будут возникать ошибки, когда я возвращаю дочерние элементы:

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

}

Как и в случае с другими здесь, если я изменю его на return <>{children}</>; , он сработает, но будет казаться грубым и ненужным.

У меня _точно_ та же проблема, что и у @kyrstenkelly : функция HOC передается React.FC , которая отображает ее дочерние элементы.

Исправление <> children(...) </> работает, но есть ли лучшее решение?

Тоже есть такая проблема! В настоящее время мы предлагаем решение для упаковки решения с помощью React.Fragment.

То же самое

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


Кажется, я перепробовал все, что здесь советовали... :( Любая подсказка?

Решение заключается в том, что вам нужно обернуть его фрагментом.

import React from 'react'

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

это исправит ошибку Type '({ children }: { children?: ReactNode; }) => ReactNode' is not assignable to type 'FunctionComponent .

Если возможно, я хотел бы избежать фрагментов и сделать так, чтобы система типов JSX поддерживала те же типы конструкций, что и JavaScript и Flow React, что помогает TypeScript лучше работать с шаблонами сторонних библиотек.

Еще один неидеальный вариант, который я нахожу, заключается в том, что фрагменты углубляют дерево компонентов, особенно в компонентах-утилитах/обертках (пример: реакция-заполнитель). Приведение к React.ReactElement кажется лучшим подходом в такого рода библиотеках, но я бы предпочел не приводить.

Я попробовал подход к изменению этого типа, но застрял с проверкой типов JSX, используя функциональный компонент нового типа. У меня до сих пор есть ветка, мог бы поклясться, что сделал пиар с просьбой о помощи, не могу найти его обратно...

Хорошо, если type должен запрещать массивы узлов, это не означает, что он должен запрещать ReactText , ReactPortal , boolean , null и undefined , а также то, что делает использование ReactElement .

Какой смысл запрещать эти дополнительные типы?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги