Definitelytyped: React 组件应该返回 ReactNode,或者子组件应该是 ReactElement

创建于 2017-07-14  ·  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 $ 中,它会起作用。

在制作只是向context添加一些内容的 Provider 组件时,返回children是很常见的。

最有用的评论

我目前正在将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 中返回多个元素。 即使您在某处进行,也很可能它以某种方式封装在一个中。
只有在带有 Fiber 的 React 16 中才有可能:)

不确定他们是否改变了反应行为,但 redux / react 作者展示了返回 this.props.children 以创建瘦包装器(仅将 childContext 添加到元素)的能力,如https://egghead.io/lessons/中所示

不过,react-native 确实允许您从渲染中返回一组 JSX 元素。 一种解决方法是用React.Children.only(this.props.children)包装this.props.children如果有多个元素作为根,它会抛出

对,您可以使用React.Children.onlyReactNodeReactElement都是this.props.children的有效值。

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

是有效且合理的 React 代码。

你可以在 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

我目前正在将children包装在React.Fragment中以解决此问题。

有任何更新吗?

不确定这是否比将children包装在React.Fragment中是否正确,但我很幸运这样做

return (children as React.ReactElement);

return <>{foo(}</>;整晚。 <>是一种解决方法。

这也给我带来了问题。

TL;博士:

我有一个向给定组件添加道具的 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 _exact_ 相同的问题 - 一个 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 更好地与 3rd 方库模式一起工作。

我发现的另一个不理想是片段加深了组件树,尤其是在实用程序/包装器组件中(案例:react-placeholder)。 在这类库中,转换为React.ReactElement似乎是一种更好的方法,但我宁愿不需要转换。

我尝试了一种方法来更改该类型,但使用新类型的功能组件进行 JSX 类型检查时卡住了。 我仍然有分支,可以发誓我做了一个 PR 寻求帮助,找不到它......

好的,如果类型需要禁止节点数组,这并不意味着它应该禁止ReactTextReactPortalbooleannullundefined以及ReactElement的用途。

禁止这些额外类型的理由是什么?

此页面是否有帮助?
0 / 5 - 0 等级