reactjs - Fragment, null, string vb. döndüren React Fonksiyon bileşenleri için bir tip tanımı var mı?
Benim sorum, bu yüksek oy almış soruyla ilgili bir açıklama/güncellemedir:
JSX.Element, ReactNode ve ReactElement'i ne zaman kullanmalı?
Bana öyle geliyor ki TypeScript, string, null gibi ilkel değerler döndüren fonksiyon bileşenlerine sahip olmak istediğinizde iyi çalışmayacak.
Örneğin, render prop isteyen bir bileşeniniz olduğunu varsayın:
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>
}
Artık bu render özelliğini dört farklı şekilde yerine getirmesi gereken bileşenler oluşturabilirim:
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;
}
Ancak, o FunctionBasedUserPnelThatReturnsAString React.ComponentType<{user: User}>; ile uyumlu değil; çünkü React.FunctionComponent bir React.ReactElement döndürmek zorundadır, bir string ise bu değildir.
React.ClassComponent diğer yandan, bu sınırlamaya sahip değildir.
const Main = () => {
return <div>
<UserProfilePage RenderUserPanel = {ClassBasedUserPanel}/>
<UserProfilePage RenderUserPanel = {ClassBasedUserPanelThatReturnsString}/>
<UserProfilePage RenderUserPanel = {FunctionBasedUserPanel}/>
{/* ERROR! */}
<UserProfilePage RenderUserPanel = {FunctionBasedUserPanelThatReturnsString}/>
</div>
}
Temelde, istediğim şey şu türden bir şey:
type TypeImLookingFor<P = {}> = React.ComponentType<P> | (props: P) => ReactNode;
Bu mevcut mu? Var olmaması için bir sebep var mı?
Not: Şu anda React 16 kullanıyorum. Belki bu, React 17 veya 18'de düzeltilmiş bir şeydir.
Cevap #1
Şimdi bunun için React 16 ile kendi en iyi çözümümü paylaşacağım - ama diğer yanıtları da memnuniyetle karşılıyorum, özellikle React 17/18’den güncellemeler varsa.
Önerdiğim gibi türü genişletmekle ilgili sorun, bunun aslında işe yaramaması.
Bir örnek olarak:
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>;
}
Yani, eğer bir fonksiyon bileşenini 'JSX tarzında' kullanmak istiyorsanız - bir string, sayı vb. döndüremezsiniz, oysaki bunu bir sınıf bileşeni ile yapabilirdiniz.
Bence o zaman en iyi çözüm şudur:
- Fonksiyon bileşenlerinizin dönüş tipini açıkça
React.ReactElementolarak belirtin - Eğer bir string, sayı vb. döndürüyorlarsa, bunu bir fragment içine sarın
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>;
}
