typescript 什么时候使用 JSX.Element、ReactNode 和 ReactElement?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/58123398/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 05:51:15  来源:igfitidea点击:

When to use JSX.Element vs ReactNode vs ReactElement?

javascriptreactjstypescript

提问by Golo Roden

I am currently migrating a React application to TypeScript. So far, this works pretty well, but I have a problem with the return types of my renderfunctions respectively my function components.

我目前正在将 React 应用程序迁移到 TypeScript。到目前为止,这工作得很好,但是我的render函数的返回类型和我的函数组件有问题。

So far, I had always used JSX.Elementas return type, now this doesn't work any more if a component decides to notrender anything, i.e. return null, since nullis no valid value for JSX.Element. This was the beginning of my journey, because now I searched the web and found that you should use ReactNodeinstead, which also includes nulland also a few other things that can happen. This seemed to be the better bet.

到目前为止,我一直用JSX.Element的返回类型,现在这不工作了,如果一个组件决定呈现任何东西,也就是回报null,因为null是没有有效的价值JSX.Element。这是我旅程的开始,因为现在我搜索了网络,发现您应该使用ReactNode它,其中还包括null以及其他一些可能发生的事情。这似乎是更好的选择。

However, now when creating a function component, TypeScript complains about the ReactNodetype. Again, after some searching I found, that for function components you should use ReactElementinstead. However, if I do so, the compatibility issue is gone, but now TypeScript again complains about nullnot being a valid value.

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

So, to cut a long story short, I have three questions:

所以,长话短说,我有三个问题:

  1. What is the difference between JSX.Element, ReactNodeand ReactElement?
  2. Why do the rendermethods of class components return ReactNode, but function components return ReactElement?
  3. How do I solve this with respect to null?
  1. 之间有什么区别JSX.ElementReactNodeReactElement
  2. 为什么render类组件的方法返回ReactNode,而函数组件返回ReactElement
  3. 我如何解决这个问题null

回答by Jonas Wilms

What is the difference between JSX.Element, ReactNode and ReactElement?

JSX.Element、ReactNode 和 ReactElement 之间有什么区别?

A ReactElement is an object with a type and props.

ReactElement 是一个具有类型和道具的对象。

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

A ReactNode is a ReactElement, a ReactFragment, a string, a number or an array of ReactNodes, or null, or undefined, or a boolean:

一个 ReactNode 是一个 ReactElement、一个 ReactFragment、一个字符串、一个数字或一个 ReactNode 数组,或者 null,或者 undefined,或者一个布尔值:

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 is a ReactElement, with the generic type for props and type being any. It exists, as various libraries can implement JSX in their own way, therefore JSX is a global namespace that then gets set by the library, React sets it like this:

JSX.Element 是一个 ReactElement,props 和 type 的泛型类型是 any。它存在,因为各种库可以以自己的方式实现 JSX,因此 JSX 是一个全局命名空间,然后由库设置,React 将其设置为:

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

By example:

举例:

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

Why do the render methods of class components return ReactNode, but function components return ReactElement?

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

Indeed, they do return different things. Components return:

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

 render(): ReactNode;

And functions are "stateless components":

函数是“无状态组件”:

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

This is actually due to historical reasons.

这其实是有历史原因的

How do I solve this with respect to null?

我如何解决这个关于 null 的问题?

Type it as ReactElement | nulljust as react does. Or let Typescript infer the type.

键入它ReactElement | null一样不作出反应。或者让 Typescript 推断类型。

source for the types

类型的来源

回答by ford04

1.) What is the difference between JSX.Element, ReactNode and ReactElement?

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

ReactElement and JSX.Elementare the result of invoking React.createElementdirectly or via JSX transpilation. It is an object with type, propsand key. JSX.Elementis ReactElement, whose propsand typehave type any, so they are more or less the same.

ReactElement 和 JSX.ElementReact.createElement直接调用或通过 JSX 转译调用的结果。它是一个带有type,props和的对象keyJSX.Elementis ReactElement, whichpropstypehave type any,所以它们或多或少是一样的。

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

ReactNodeis used as return type for render()in class components. It also is the default type for childrenattribute with PropsWithChildren.

ReactNode用作render()类组件的返回类型。它也是children属性的默认类型PropsWithChildren

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

It looks more complicated in the React type declarations, but is equivalentto:

它在React 类型声明中看起来更复杂,但等效于:

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

You can assign almost everything to ReactNode. I usually would prefer stronger types, but there might be some valid cases to use it.

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



2.) Why do the render methods of class components return ReactNode, but function components return ReactElement?

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

tl;dr:It is a current TS type incompatibility not related to core React.

tl;dr:这是与核心 React 无关的当前 TS 类型不兼容。

  • TS class component: returns ReactNodewith render(), more permissive than React/JS

  • TS function component: returns JSX.Element | null, more restrictive than React/JS

  • TS类组件:返回ReactNoderender()比阵营/ JS更宽容

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

In principle, render()in React/JS class components supports the same return typesas a function component. With regard to TS, the different types are a type inconsistency still kept due to historical reasons and the need for backwards-compatibility.

原则上,render()React/JS 类组件支持与函数组件相同的返回类型。对于TS,不同类型是由于历史原因和向后兼容的需要仍然保持的类型不一致。

Ideally a valid return typewould probably look more like this:

理想情况下,有效的返回类型可能看起来更像这样:

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


3.) How do I solve this with respect to null?

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

Note:Avoiding React.FCwon't saveyou from the JSX.Element | nullreturn type restriction.

注意:避免React.FC不会使您免于JSX.Element | null返回类型限制。

Create React App recently dropped React.FCfrom its template, as it has some quirks like an implicit {children?: ReactNode}type definition. So using React.FCsparingly might be preferable.

Create React App 最近从其模板中删除React.FC,因为它有一些像隐式{children?: ReactNode}类型定义这样的怪癖。因此,React.FC谨慎使用可能更可取。

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