score:507
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.
score:4
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
}
score:5
ReactElement is the type for elements in React, either created via JSX or React.createElement.
const a = <div/> // this is a ReactElement
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.
const a = (
<div>
hello {[1, "world", true]} // this is a ReactNode
</div>
)
JSX.Element
is an internal hook for Typescript. It is set equal to ReactElement to tell Typescript that every JSX expressions should be typed as ReactElements. But if we'd use Preact, or other technologies using JSX it would be set to something else.
Functional components return ReactElement | null
, so it cannot return a bare string or an array of ReactElements. It is a known limitation. The workaround is to use Fragments :
const Foo: FC = () => {
return <>hello world!</> // this works
}
Class components' render function return ReactNode
, so there shouldn't be any problem.
score:70
1.) What is the difference between JSX.Element, ReactNode and ReactElement?
ReactElement and JSX.Element
are the result of invoking React.createElement
directly or via JSX transpilation. It is an object with type
, props
and key
. JSX.Element
is ReactElement
, whose props
and type
have type any
, so they are more or less the same.
const jsx = <div>hello</div>
const ele = React.createElement("div", null, "hello");
ReactNode is used as return type for render()
in class components. It also is the default type for children
attribute with PropsWithChildren
.
const Comp: FunctionComponent = props => <div>{props.children}</div>
// children?: React.ReactNode
It looks more complicated in the React type declarations, but is equivalent to:
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...)
You can assign almost everything to ReactNode
. I usually would prefer stronger types, but there might be some valid cases to use it.
2.) Why do the render methods of class components return ReactNode, but function components return ReactElement?
tl;dr: It is a current TS type incompatibility not related to core React.
TS class component: returns
ReactNode
withrender()
, more permissive than React/JSTS function component: returns
JSX.Element | null
, more restrictive than React/JS
In principle, render()
in React/JS class components supports the same return types as a function component. With regard to TS, the different types are a type inconsistency still kept due to historical reasons and the need for backwards-compatibility.
Ideally a valid return type would probably look more like this:
type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number
| boolean | null // Note: undefined is invalid
Some options:3.) How do I solve this with respect to 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>;
Note: Avoiding React.FC
won't save you from the JSX.Element | null
return type restriction.
Create React App recently dropped React.FC
from its template, as it has some quirks like an implicit {children?: ReactNode}
type definition. So using React.FC
sparingly might be preferable.
const MyCompFragment: FunctionComponent = () => <>"Hello"</>
const MyCompCast: FunctionComponent = () => "Hello" as any
// alternative to `as any`: `as unknown as JSX.Element | null`
Source: stackoverflow.com
Related Query
- When to use JSX.Element vs ReactNode vs ReactElement?
- Cannot use JSX unless the '--jsx' flag is provided when "jsx" is "react-jsx"
- When to use anonymous functions in JSX
- How to use useRef when element is inside condition?
- React+gsap use random animation element group can't work when it complete?
- Getting "JSX element type 'App' does not have any construct or call signatures." ts(2604) error when exporting array of JSX Element
- Why does a React JSX element event handler not use parentheses similar to a html event handler?
- How to get next element in Immutable List when use .map?
- JSX element type 'AddIcon' does not have any construct or call signatures when testing
- How to clear/destroy Chart.js canvas when the chart is represented as a react JSX element
- React JSX expressions must have a parent element error when adding a second <div>
- When we use JSX <div></div>, or React.createElement("div", ...), is this virtual DOM (element)?
- When I try to push an element to a array but it takes only one rest of the elements are got removed automatically. It happen when use useState hook
- How can I use an SVG element created with JSX as an image source in a Canvas?
- Getting 'Parse Error: Adjacent JSX elements must be wrapped in an enclosing tag' even when I use React.Fragment
- Why React fails to render changes in array of JSX element when using .map index as a key?
- How do we use two themes on the same DOM element when using a ThemeProvider?
- React complains element type is invalid when trying to use context
- 'React' must be in scope when using JSX but I do not use ESLint
- Ionic React Typescript with pug.js, 'Uncaught ReferenceError: React is not defined' when use tsx but not jsx
- when to and when not to use "this" in JSX event handler function reference?
- In react I had created wrapper components.So now i need to use them in my app.tsx in element parameter which is in route tag.But error as not jsx elem
- How to use .contains(nodeOrNodes) API when the contained react element has an arrow function event handler?
- Make hover effect stick in an element when mouse is pointed to adjacent element in React Jsx
- When to use ES6 class based React components vs. functional ES6 React components?
- Cannot use JSX unless the '--jsx' flag is provided
- 'React' must be in scope when using JSX react/react-in-jsx-scope?
- React Context vs React Redux, when should I use each one?
- When to use React setState callback
- react-router vs react-router-dom, when to use one or the other?
More Query from same tag
- Tutorial: Intro to React - Rerendering the whole collection instead of the most recently changed element
- After I installed nodemon by "npm i nodemon" , when I tried to run it by "nodemon server.js" , it gives me this error. What should I do?
- append javascript array data into table td
- How to type a React.createContext for typescript, which takes a ref and a setState function?
- React: Inserting a line break in localised text
- How to update dependencies version (e.g react) in package.json if using docker
- How to open a component in full screen on click of a button in react js?
- How to wait result of a function in React?
- semantic ui react mobile friendly components
- Update state on Parent component from input in Child using props and useState Hook
- Confirm Window in React
- Pass argument onClick to props.function() executed via refs
- Errors with node-mssql in a webpack-bundled React app
- Image not loading when mapping Material-UI cards
- How to convert String to something that React can render properly?
- Multiple Modal Opening when Updating a Row using React
- Uncaught TypeError: Cannot destructure property 'username' of 'match.parse' as it is undefined
- How to manage the click event to show/hide certain element in React?
- delay css animation styled-component
- React.js: React dropdown does not hold data
- How to add items to array in react
- Want to test for size of an array constant in React
- Routes not rendering with react-router-dom
- How do I parse a state variable's value into a <Link> element's target url in React?
- cant show page in children route React JS
- How to properly initialize React app with redux in Cordova - mapStateToProps not called
- Non-sequential event bubbling onClick vs addEventListener
- Why this.props is undefined inside handleLogin method?
- Call handle functions with onClick and react
- Is there a way to get all data from a firestore database using useCollectionData()?