什么时候使用JSX.Element vs ReactNode vs ReactElement?

时间:2019-09-26 19:18:45

标签: javascript reactjs typescript

我目前正在将React应用程序迁移到TypeScript。到目前为止,这种方法效果很好,但是我的render函数和函数组件的返回类型存在问题。

到目前为止,我一直使用JSX.Element作为返回类型,现在如果组件决定渲染任何东西,即返回null,则此方法将不再起作用,因为nullJSX.Element的无效值。这是我旅程的开始,因为现在我在网上搜索,发现您应该改用ReactNode,其中还包括null和可能发生的其他一些事情。这似乎是更好的选择。

但是,现在在创建函数组件时,TypeScript抱怨ReactNode类型。再次,我经过一番搜索后发现,对于功能组件,应改为使用ReactElement。但是,如果这样做,兼容性问题就消失了,但是现在TypeScript再次抱怨null不是有效值。

因此,总而言之,我有三个问题:

  1. JSX.ElementReactNodeReactElement有什么区别?
  2. 为什么类组件的render方法返回ReactNode,而函数组件返回ReactElement
  3. 如何针对null解决此问题?

2 个答案:

答案 0 :(得分:27)

1。)JSX.Element,ReactNode和ReactElement有什么区别?

ReactElement和JSX.Element 是直接或通过JSX转换调用React.createElement的结果。它是带有typepropskey的对象。 JSX.ElementReactElement,其propstype的类型为any,因此它们大致相同。

const jsx = <div>hello</div>
const ele = React.createElement("div", null, "hello");

ReactNode 在类组件中用作render()的返回类型。也是PropsWithChildrenchildren属性的默认类型。

const Comp: FunctionComponent = props => <div>{props.children}</div> 
// children?: React.ReactNode

React type declarations中看起来更复杂,但与等价等同于:

type ReactNode = {} | null | undefined;
// super type `{}` has absorbed *all* other types, which are sub types of `{}`
// so it is a very "broad" type (I don't want to say useless...)

您几乎可以将所有内容分配给ReactNode。我通常希望使用更强的类型,但可能会有一些有效的情况来使用它。


2。)为什么类组件的render方法返回ReactNode,而功能组件返回ReactElement?

tl; dr:。这是当前的TS类型不兼容not related to core React

  • TS类组件:返回ReactNoderender(),比React / JS更宽松

  • TS函数组件:返回JSX.Element | null,比React / JS更具限制性

原则上,React / JS类组件supports the same return types中的render()作为函数组件。对于TS,由于历史原因和向后兼容性的需要,不同类型的类型仍然保持不一致。

理想情况下,valid return type可能更像这样:

type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number 
  | boolean | null // Note: undefined is invalid

3。)我该如何解决null问题?

一些选项:
// Use type inference; inferred return type is `JSX.Element | null`
const MyComp1 = ({ condition }: { condition: boolean }) =>
    condition ? <div>Hello</div> : null

// Use explicit function return types; Add `null`, if needed
const MyComp2 = (): JSX.Element => <div>Hello</div>; 
const MyComp3 = (): React.ReactElement => <div>Hello</div>;  
// Option 3 is equivalent to 2 + we don't need to use a global (JSX namespace)

// Use built-in `FunctionComponent` or `FC` type
const MyComp4: React.FC<MyProps> = () => <div>Hello</div>;

注意:避免React.FC返回类型限制JSX.Element | null won't save

最近从其模板dropped React.FC创建React App,因为它具有一些隐式{children?: ReactNode}类型定义等怪癖。因此,最好不要使用React.FC

在极端情况下,您可以添加类型断言或片段作为解决方法:
const MyCompFragment: FunctionComponent = () => <>"Hello"</>
const MyCompCast: FunctionComponent = () => "Hello" as any 
// alternative to `as any`: `as unknown as JSX.Element | null`

答案 1 :(得分:20)

  

JSX.Element,ReactNode和ReactElement有什么区别?

ReactElement是具有类型和属性的对象。

 interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;
}

ReactNode是ReactElement,ReactFragment,ReactNodes的字符串,数字或数组,或者为null,未定义或布尔值:

type ReactText = string | number;
type ReactChild = ReactElement | ReactText;

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

JSX.Element是一个ReactElement,props的泛型类型是任意类型。它存在,因为各种库都可以以自己的方式实现JSX,因此JSX是一个全局名称空间,然后由该库设置,React对其进行如下设置:

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
  }
}

例如:

 <p> // <- ReactElement = JSX.Element
   <Custom> // <- ReactElement = JSX.Element
     {true && "test"} // <- ReactNode
  </Custom>
 </p>
  

为什么类组件的render方法返回ReactNode,而函数组件返回ReactElement?

实际上,它们确实返回不同的东西。 Component的回报:

 render(): ReactNode;

函数是“无状态组件”:

 interface StatelessComponent<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement | null;
    // ... doesn't matter
}

如您所见,功能组件可以将ReactNodes作为子组件,但是它们必须返回ReactElement或null。如果您考虑一下,它实际上与组件相同:它们是本身 ReactElement,但是它们可以有ReactNode作为子级。

  

如何解决null问题?

与react一样,将其键入为ReactElement | null。或者让Typescript推断类型。

source for the types