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. 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
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:
- Explicitly state the return type of your function components as
React.ReactElement
- 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
How to Remove Element from an Array in JavaScript
Vuejs 2.0 Emit Event from Grand Child to His Grand Parent Component
How to Calculate 3D Rotation on X and Y Axis from a 4X4 Matrix
Var Name Produces Strange Result in JavaScript
How to Clone a JavaScript Object Except for One Key
Fastest Way to Check a String Contain Another Substring in JavaScript
How to Declare a Global Variable in JavaScript
Classical Inheritance VS Prototypal Inheritance in JavaScript
Node.Js Tail-Call Optimization: Possible or Not
How to Print a Stack Trace in Node.Js
Plain Count Up Timer in JavaScript
How to Require a Controller in an Angularjs Directive
What Are the Semantics of Different Rxjs Subjects
Display a Popup Only Once Per User
What Is This Practice Called in JavaScript
What Does ${} (Dollar Sign and Curly Braces) Mean in a String in JavaScript