JSX.Element, ReactNode vs ReactElement ne zaman kullanılır?


142

Şu anda bir React uygulamasını TypeScript'e geçiriyorum. Şimdiye kadar, bu oldukça iyi çalışıyor, ancak renderişlevlerimin ve işlev bileşenlerimin dönüş türleriyle ilgili bir sorunum var .

Şimdiye kadar, hep kullanmıştı JSX.Elementbir bileşen karar verirse şimdi bu artık çalışmıyor, dönüş türü olarak değil bir şey, yani geri dönüşü hale nullberi nullhiçbir geçerli bir değerdir JSX.Element. Bu benim yolculuğumun başlangıcıydı, çünkü şimdi internette arama yaptım ve ReactNodebunun yerine kullanmanız gerektiğini buldum , bu da nullolabilecek birkaç şeyi ve ayrıca olabilecek birkaç şeyi de içeriyor . Bu daha iyi bir bahis gibi görünüyordu.

Ancak, şimdi bir işlev bileşeni oluştururken, TypeScript türden şikayet ediyor ReactNode. Yine, biraz araştırdıktan sonra, bunun yerine işlev bileşenleri için kullanmanız gerektiğini buldum ReactElement. Ancak, bunu yaparsam uyumluluk sorunu ortadan kalktı, ancak şimdi TypeScript yine nullgeçerli bir değer olmadığından şikayet ediyor .

Yani, uzun lafın kısası, üç sorum var:

  1. Ne arasındaki farktır JSX.Element, ReactNodeve ReactElement?
  2. Neden rendersınıf bileşenlerinin yöntemleri geri dönerken ReactNode, işlev bileşenleri geri dönüyor ReactElement?
  3. Bunu ile ilgili olarak nullnasıl çözerim ?

Genellikle bileşenlerle dönüş türünü belirtmeniz gerekmediğinden, bunun ortaya çıkmasına şaşırdım. Bileşenler için ne tip imza yapıyorsunuz? class Example extends Component<ExampleProps> {Sınıflar ve const Example: FunctionComponent<ExampleProps> = (props) => {işlev bileşenleri gibi bir şeye benzemelidir (burada ExamplePropsbeklenen aksesuarlar için bir arayüz bulunur). Ve sonra bu türler, dönüş türünün çıkarılabileceği konusunda yeterli bilgiye sahiptir.
Nicholas Kule


1
@NicholasTower Linting kurallarımız açıkça dönüş türünü sağlamayı zorunlu kılar ve bu nedenle ortaya çıkar (ki bu IMHO iyi bir şeydir, çünkü derleyicinin her şeyi çıkarmasına izin vermektense ne yaptığınız hakkında daha fazla düşünür, anlamaya yardımcı olur) .
Golo Roden

Yeterince adil, kural koymayı düşünmedim.
Nicholas Kule

@JonasWilms Bağlantı için teşekkürler, ancak bunun sorularıma cevap verdiğini sanmıyorum.
Golo Roden

Yanıtlar:


154

JSX.Element, ReactNode ve ReactElement arasındaki fark nedir?

Bir ReactElement, bir türü ve propları olan bir nesnedir.

 interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;
}

Bir ReactNode, bir ReactElement, bir ReactFragment, bir dizi, bir sayı veya ReactNode dizisi veya null veya undefined veya bir boole'dur:

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 bir ReactElement olup, props için jenerik tip ve herhangi biri olabilir. Çeşitli kütüphaneler JSX'i kendi yöntemleriyle uygulayabildiğinden mevcuttur, bu nedenle JSX daha sonra kütüphane tarafından belirlenen global bir ad alanıdır, React bunu şu şekilde ayarlar:

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
  }
}

Örnek olarak:

 <p> // <- ReactElement = JSX.Element
   <Custom> // <- ReactElement = JSX.Element
     {true && "test"} // <- ReactNode
  </Custom>
 </p>

Sınıf bileşenlerinin render yöntemleri neden ReactNode'u döndürürken, işlev bileşenleri ReactElement'i döndürüyor?

Nitekim farklı şeyler verirler. Componentdönüş:

 render(): ReactNode;

Ve işlevler "durumsuz bileşenlerdir":

 interface StatelessComponent<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement | null;
    // ... doesn't matter
}

Bu aslında tarihsel nedenlerden kaynaklanmaktadır .

Bunu null ile ilgili olarak nasıl çözerim?

Olarak yazın ReactElement | nulltıpkı yapar tepki verir. Veya Typecript'in türü çıkarmasına izin verin.

türler için kaynak


Detaylı cevap için teşekkürler! Bu, 1. soruyu mükemmel şekilde yanıtlar, ancak yine de 2. ve 3. soruları cevapsız bırakır. Onlara da rehberlik edebilir misiniz lütfen?
Golo Roden

@goloRoden eminim, şu anda biraz yorgunum ve cep telefonundaki türler arasında gezinmek biraz zaman alıyor ...;)
Jonas Wilms

Merhaba @JonasWilms. "Onlar değil. ReactComponent şu şekilde tanımlanır: render (): JSX.Element | null | false;", bunu nerede görüyorsunuz? Görünüşe göre bana ReactNode döndürüyor ( github.com/DefinitelyTyped/DefinitelyTyped/blob/… ) Ayrıca küçük yazım hatası, bence veya ReactComponentolmalı . React.ComponentComponent
Mark Doliner

@MarkDoliner Tuhaf, o türü dosyadan kopyaladığıma yemin edebilirim ... Her neyse, kesinlikle haklısın, düzenleyeceğim
Jonas Wilms

Çocuklar, web'de herhangi bir yerde react && typcript ile ilgili herhangi bir resmi belge var mı?
Goran_Ilic_Ilke

25

1.) JSX.Element, ReactNode ve ReactElement arasındaki fark nedir?

ReactElement ve JSX.Element , React.createElementdoğrudan veya JSX aktarımı aracılığıyla çağırmanın sonucudur. Bu sahip bir nesnedir type, propsve key. JSX.Elementolduğu ReactElementkimin, propsve typetürüne sahip anydaha az ya da aynıdır, böylece.

const jsx = <div>hello</div>
const ele = React.createElement("div", null, "hello");

ReactNode , render()sınıf içi bileşenler için dönüş türü olarak kullanılır . Ayrıca, childrenile özniteliği için varsayılan türdür PropsWithChildren.

const Comp: FunctionComponent = props => <div>{props.children}</div> 
// children?: React.ReactNode

React türü bildirimlerinde daha karmaşık görünür , ancak şuna eşdeğerdir :

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...)

Hemen hemen her şeyi atayabilirsiniz ReactNode. Genelde daha güçlü tipleri tercih ederim, ancak kullanmak için bazı geçerli durumlar olabilir.


2.) Sınıf bileşenlerinin render yöntemleri neden ReactNode'u döndürürken, işlev bileşenleri ReactElement'i döndürüyor?

tl; dr: Core React ile ilgili olmayan güncel bir TS tipi uyumsuzluktur .

  • TS sınıfı bileşeni: döner ReactNodeile render(), tepki / JS daha geçirgen

  • TS fonksiyon bileşeni: döner JSX.Element | null, React / JS'den daha kısıtlayıcıdır

Prensipte, render()React / JS sınıfındaki bileşenler , bir fonksiyon bileşeni ile aynı dönüş tiplerini destekler . TS ile ilgili olarak, farklı türler, tarihsel nedenlerden ve geriye dönük uyumluluk ihtiyacı nedeniyle hala tutulan bir tür tutarsızlığıdır.

İdeal olarak geçerli bir dönüş türü muhtemelen daha çok şuna benzer:

type ComponentReturnType = ReactElement | Array<ComponentReturnType> | string | number 
  | boolean | null // Note: undefined is invalid

3.) Bunu null ile ilgili olarak nasıl çözerim?

Bazı seçenekler:
// 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>;

Not: Önlemek sizi dönüş türü kısıtlamasından React.FC kurtarmazJSX.Element | null .

Create React App , örtük bir tür tanımı gibi bazı tuhaflıkları olduğu için kısa süre önce şablonundan çıkarıldıReact.FC{children?: ReactNode} . Bu yüzden React.FCidareli kullanmak tercih edilebilir.

Uç durumlarda, geçici çözüm olarak bir tür iddiası veya Parçalar ekleyebilirsiniz:
const MyCompFragment: FunctionComponent = () => <>"Hello"</>
const MyCompCast: FunctionComponent = () => "Hello" as any 
// alternative to `as any`: `as unknown as JSX.Element | null`
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.