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 render
functions, specifically in my functional components.
I have always used JSX.Element
as the return type, now this doesn't work any more if a component decides to not render anything, i.e. returns null
, since null
is not a valid value for JSX.Element
. This was the beginning of my journey. I searched the web and found that you should use ReactNode
instead, which includes null
and a few other things that can happen.
However, when creating a functional component, TypeScript complains about the ReactNode
type. Again, after some searching I found, that for functional components you should use ReactElement
instead. However, if I do so, the compatibility issue is gone, but now TypeScript again complains about null
not being a valid value.
To cut a long story short, I have three questions:
JSX.Element
, ReactNode
and ReactElement
?render
methods of class components return ReactNode
, but functional components return ReactElement
?null
?ReactElement is the type for elements in React, either created via JSX or React. createElement. ReactNode is wider, it can be text, number, boolean, null, undefined, a portal, a ReactElement, or an array of ReactNodes. It represents anything that React can render.
JSX is an embeddable XML-like syntax. It is meant to be transformed into valid JavaScript, though the semantics of that transformation are implementation-specific. JSX rose to popularity with the React framework, but has since seen other implementations as well.
React doesn't require using JSX, but most people find it helpful as a visual aid when working with UI inside the JavaScript code. It also allows React to show more useful error and warning messages. With that out of the way, let's get started!
A React node is defined as: a light, stateless, immutable, virtual representation of a DOM node. React nodes are not real DOM nodes (e.g., text or element nodes) themselves, but a representation of a potential DOM node. The representation is considered the virtual DOM.
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. Component
s 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With