何时使用JSX.Element vs ReactNode vs ReactElement?

在React开发中,JSX.ElementReactNodeReactElement 这三个类型分别代表不同级别的React组件树中的元素,它们在不同的上下文中有着各自的用途。
以下是它们的区别及使用场景的概述:

JSX.Element

定义
JSX.Element 是当你编写 JSX 语法时,编译器(如Babel)将这些语法转化为等效的 React.createElement() 调用所返回的对象类型。例如,以下 JSX 代码:

const myElement = <div>Hello, World!</div>;

在编译后实际上会变为:

const myElement = React.createElement("div", null, "Hello, World!");

这里的 myElement 类型就是 JSX.Element

使用场景

  • 作为组件的返回值:在React组件中,当你直接返回一个JSX表达式(如 <div>...</div>),该组件的返回类型就是 JSX.Element
  • 作为函数参数:当某个函数接受一个React元素作为参数时,可以将其类型声明为 JSX.Element。例如,一个负责渲染特定元素的高阶组件(HOC)可能有这样的签名:
    function withSomeEnhancement(WrappedComponent: React.ComponentType): (props: Props) => JSX.Element { ... }
    
  • 作为数组元素或对象属性:当需要存储或传递一系列React元素(如在数组中存储多个子组件,或在对象字面量中作为属性值)时,这些元素的类型应为 JSX.Element

ReactNode

定义
ReactNode 是一个更宽泛的类型,它包含了所有React认为合法的“节点”,不仅包括 JSX.Element,还包括以下几种类型:

  • 字符串(string
  • 数字(number
  • 布尔值(boolean
  • nullundefined
  • ReactFragment(由数组或<>...</>语法创建的多个并列子元素)
  • ReactPortal(用于将子元素插入到DOM的其他位置,如ReactDOM.createPortal()返回的类型)

使用场景

  • 作为组件的children属性:当一个组件允许接收任意类型的子元素(不仅仅是单一的React元素)时,其 children 属性类型通常被声明为 ReactNode
    这样可以接收字符串、数字、布尔值、空值、React元素数组、Fragments等。
  • 泛型约束:在需要处理可能包含多种React节点类型的集合或结构时,可以使用 ReactNode 作为泛型约束,确保这些结构只包含React认可的节点类型。

ReactElement

定义
ReactElement 是React组件树中的基础构建块,是一个JavaScript对象,表示一个具体的React组件实例及其相关的属性和子元素。它的结构通常如下:

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

其中:

  • type:表示组件类型,可以是字符串(HTML标签名)或一个React组件构造函数。
  • props:一个对象,包含传递给组件的所有属性和方法。
  • key:可选的,用于React内部的高效更新和排序。

使用场景

  • 低级别操作:直接操作React组件树(如在自定义的shouldComponentUpdateReact.Children.map等方法中)时,可能会遇到 ReactElement 对象。
  • 类型细化:在需要确保变量或参数具体为React组件实例(而非其他ReactNode类型)时,可以使用 ReactElement 类型。
    尽管在大多数情况下,JSX.Element 已足够,但在某些涉及更底层React API或高级类型技巧的场景中,可能需要明确使用 ReactElement

总结来说:

  • JSX.Element:用于表示由JSX编译出的单个React元素,常用于组件返回值、函数参数和数据结构。
  • ReactNode:涵盖所有React允许的节点类型,包括但不限于React元素、基本类型值、Fragments和Portals,常用于组件的children属性和需要处理多种节点类型的情况。
  • ReactElement:最底层的React组件实例表示,用于直接操作组件树或在需要精确类型控制时使用。
    在实际编码中,通常较少直接指定为 ReactElement 类型,更多使用 JSX.Element

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

事实上,他们确实返回了不同的东西。组件返回:
render(): ReactNode;

函数是“无状态组件”:

 interface StatelessComponent<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement | null;
    // ... doesn't matter
}
posted @ 2024-04-21 14:45  龙陌  阅读(565)  评论(0编辑  收藏  举报