My question is a clarification/update on this highly upvoted question:
When to use JSX.Element vs ReactNode vs ReactElement?
It seems to me that TypeScript is not going to play well when you want to have function components that return primitives like string, null etc.
For example, say you have a component that wants a render prop:
import React from "react";
type User = {
id: string;
name: string;
imgSrc: string;
}
type UserProfilePageProps = {
RenderUserPanel: React.ComponentType<{user: User}>;
}
export const fetchUser = async () : Promise<User> => {
return {
id: "123",
imgSrc: "placeholdit",
name: "Joe Bloggs"
}
}
const UserProfilePage = (props: UserProfilePageProps) => {
const {RenderUserPanel} = props;
const [user, setUser] = React.useState<null |User>(null);
React.useEffect(()=> {
fetchUser().then(user => setUser(user));
}, []);
return <section>
<h2>User Details</h2>
{user && <RenderUserPanel user = {user}/>}
</section>
}
Now I can create components that should fulfil this render prop in four different ways:
class ClassBasedUserPanel extends React.Component<{user: User}> {
render() {
return <div>
{this.props.user.name}
</div>
}
}
class ClassBasedUserPanelThatReturnsString extends React.Component<{user: User}> {
render() {
return this.props.user.name;
}
}
const FunctionBasedUserPanel = (props: {user: User}) => {
return <div>
{props.user.name}
</div>
}
const FunctionBasedUserPanelThatReturnsString = (props: {user: User}) => {
return props.user.name;
}
However, that FunctionBasedUserPnelThatReturnsAString does not fit React.ComponentType<{user: User}>; because React.FunctionComponent must return a React.ReactElement which a string is not.
React.ClassComponent on the otherhand, does not have this constraint.
const Main = () => {
return <div>
<UserProfilePage RenderUserPanel = {ClassBasedUserPanel}/>
<UserProfilePage RenderUserPanel = {ClassBasedUserPanelThatReturnsString}/>
<UserProfilePage RenderUserPanel = {FunctionBasedUserPanel}/>
{/* ERROR! */}
<UserProfilePage RenderUserPanel = {FunctionBasedUserPanelThatReturnsString}/>
</div>
}
Essentially, what I want is a type that is:
type TypeImLookingFor<P = {}> = React.ComponentType<P> | (props: P) => ReactNode;
Does this exist? Is there a reason that it shouldn't exist?
Note: I'm currently using React 16. Perhaps this is something that is fixed in React 17 or 18.