ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我一直使用 JSX.Element 作为返回类型,现在如果组件决定 呈现任何内容,即返回 null,这将不再起作用,因为 null 不是有效的JSX.Element 的值。这是我旅程的开始。我在网上搜索,发现您应该改用 ReactNode,其中包括 null 和一些其他可能发生的事情。

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

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

JSX.Element、ReactNode 和 ReactElement 有什么区别?为什么类组件的render方法返回ReactNode,而功能组件返回ReactElement?关于null,我该如何解决这个问题?

我很惊讶会出现这种情况,因为通常您不需要使用组件指定返回类型。你为组件做什么类型的签名?对于类应该类似于 class Example extends Component<ExampleProps> {,对于函数组件应该类似于 const Example: FunctionComponent<ExampleProps> = (props) => {(其中 ExampleProps 是预期道具的接口)。然后这些类型有足够的信息可以推断出返回类型。
类型已定义 here
@NicholasTower 我们的 linting 规则强制明确提供返回类型,这就是出现这种情况的原因(恕我直言,这是一件好事,因为您对自己所做的事情的思考更多,这有助于理解,而不是让编译器推断一切) .
很公平,我没有想到 linting 规则。
@JonasWilms 感谢您的链接,但我认为这不能回答我的问题。

l
liuliang

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

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

 type Key = string | number

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

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 是一个 ReactElement,props 的泛型类型是 any。它存在,因为各种库可以以自己的方式实现 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
}

这实际上是由于 historical reasons

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

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

source for the types


感谢您的详细回答!这完美地回答了问题 1,但仍然没有回答问题 2 和 3。你也可以提供一些关于它们的指导吗?
@goloRoden 当然,我现在只是有点累,需要一些时间来滚动浏览手机上的类型......;)
嗨@JonasWilms。关于“他们没有。ReactComponent 被定义为:render(): JSX.Element | null | false;”,你在哪里看到的?看起来它向我返回了 ReactNode (github.com/DefinitelyTyped/DefinitelyTyped/blob/…) 也是轻微的错字,我认为 ReactComponent 应该是 React.ComponentComponent
@MarkDoliner 很奇怪,我可以发誓我从文件中复制了那个类型......无论如何,你是完全正确的,我会编辑
伙计们,网络上是否有任何关于 react && typescript 的官方文档?
C
Community

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类组件:用render()返回ReactNode,比React/JS更宽松

TS 函数组件:返回 JSX.Element | null,比 React/JS 更严格

原则上,React/JS 类组件中的 render() supports the same return types 作为函数组件。对于 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 won't save 你受到 JSX.Element | null 返回类型的限制。

最近从其模板创建 React App dropped 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`

这个答案很棒。但是,我在 2022 年使用 React 18。ReactFragment 现在是 Iterable<ReactNode>,不再有类型 {}
E
Emmanuel Meric de Bellefon

ReactElement 是 React 中元素的类型,通过 JSX 或 React.createElement 创建。

const a = <div/> // this is a ReactElement

ReactNode 更广泛,它可以是文本、数字、布尔值、null、未定义、门户、ReactElement 或 ReactNode 数组。它代表 React 可以渲染的任何东西。

const a = (
  <div>
     hello {[1, "world", true]} // this is a ReactNode
  </div>
)

JSX.Element 是 Typescript 的内部挂钩。 设置等于 ReactElement 告诉 Typescript 每个 JSX 表达式都应该被输入为 ReactElements。但是如果我们使用 Preact,或者其他使用 JSX 的技术,它就会被设置为其他东西。

功能组件返回 ReactElement | null,因此它不能返回裸字符串或 ReactElements 数组。它是一个 known limitation。解决方法是使用 Fragments

const Foo: FC = () => {
  return <>hello world!</>  // this works
}

类组件的渲染函数返回ReactNode,所以应该没有问题。


H
Hossam Mourad

https://github.com/typescript-cheatsheets/react#useful-react-prop-type-examples

export declare interface AppProps {
  children1: JSX.Element; // bad, doesnt account for arrays
  children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings
  children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility
  children4: React.ReactChild[]; // better, accepts array children
  children: React.ReactNode; // best, accepts everything (see edge case below)
  functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type
  style?: React.CSSProperties; // to pass through style props
  onChange?: React.FormEventHandler<HTMLInputElement>; // form events! the generic parameter is the type of event.target
  //  more info: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring
  props: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its ref
  props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref
}