When to Use Jsx.Element VS Reactnode VS Reactelement

When to use JSX.Element vs ReactNode vs ReactElement?

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

A ReactElement is an object with a type and props.

 type Key = string | number

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:

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:

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?

Indeed, they do return different things. Components return:

 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?

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

source for the types

Typescript React.ReactElement vs JSX.Element

Even though JSX can be used in React to create React elements, React does not require JSX to work, you could replace it with vanilla JavaScript.

React elements are simply defined as a description of what you want to see on the screen. In other words, React elements are the instructions for how the browser DOM should be created.

A ReactElement is an object with a type and props. React reads these objects and uses them to construct the DOM. JSX.Element, on the other hand, is a ReactElement with generic type for props and type being any.

Type '() = JSX.Element' is not assignable to type 'ReactNode'

Since version 6, react routes no longer gets components, but elements instead.
So instead of passing component you need to pass <Component/> like this:

{publicRoutes.map(({path, component: Component}) => (
<Route path={path} element={<Component/>} /> // warning
))}

Is there a typing for React Function components that includes returning fragments, nulls, strings etc?

I'm going to post my own best solution for React 16 for this now - but I welcome other answers, especially if there are updates from React 17/18.

The problem with widening the type as I've suggested, is that it doesn't actually work.

As an example:

type TypeImLookingFor<P = {}>  = React.ComponentType<P> | ((props: P) => React.ReactNode); 

class ComponentA extends React.Component {
render() {
return "aaa";
}
}

const ComponentB = () => {
return "bbb";
}

// They do match the type signature
const A: TypeImLookingFor = ComponentA;
const B: TypeImLookingFor = ComponentB;

const Main = () => {
return <div>
<ComponentA/>

{/*
'ComponentB' cannot be used as a JSX component.
Its return type 'string' is not a valid JSX element.ts(2786) */}
<ComponentB/>
</div>;

}

That is, if you want use a function component in a 'JSX-y' fashion - you can't return a string, number etc, even though you could do that with a class component.

The best solution then, in my opinion is:

  1. Explicitly state the return type of your function components as React.ReactElement
  2. If they are returning a string, or a number etc, wrap it in a fragment
const ComponentC = () : React.ReactElement => {
//Type 'string' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.ts(2322)
return "aaa";
}

const ComponentD = () : React.ReactElement => {
return <> aaa </>
}

const Main2 = () => {
return <div>
<ComponentA/>
<ComponentD/>
</div>;

}

Getting error when using type React.ReactNode to children. Which type should I use?

React components should return JSX.Element. As you can see, your children are of type React.ReactNode, so straight returning them will not work. You need to wrap them inside an element or React.Fragment:

type TestComponentProps = {
children?: React.ReactNode;
}

function TestComponent(props: TestComponentProps) {
return <React.Fragment>{props.children}</React.Fragment>;
}

How to type Functional Components in React with @types/react: ^18.0.17?

Not sure I understand the question, but you can add children as prop like usual and define an interface for the props:

interface YourComponentProps {
children: ReactNode;
}
export const YourComponent = (props: YourComponentProps) => {
return <></>
}


Related Topics



Leave a reply



Submit