Typcript'te gerçek bir “if” gibi davranan bir React “If” bileşenini nasıl yapabilirim?


11

<If />React kullanarak basit bir işlev bileşeni yaptım :

import React, { ReactElement } from "react";

interface Props {
    condition: boolean;
    comment?: any;
}

export function If(props: React.PropsWithChildren<Props>): ReactElement | null {
    if (props.condition) {
        return <>{props.children}</>;
    }

    return null;
}

Daha temiz bir kod yazmama izin veriyor, örneğin:

render() {

    ...
    <If condition={truthy}>
       presnet if truthy
    </If>
    ...

Çoğu durumda, iyi çalışır, Ama örneğin belirli bir değişken tanımlanmamış olup olmadığını kontrol etmek ve sonra özellik olarak geçirmek istiyorum, bir sorun haline gelir. Bir örnek vereceğim:

Diyelim ki <Animal />, aşağıdaki Props'lara sahip bir bileşenim var :

interface AnimalProps {
  animal: Animal;
}

ve şimdi aşağıdaki DOM oluşturan başka bir bileşen var:

const animal: Animal | undefined = ...;

return (
  <If condition={animal !== undefined} comment="if animal is defined, then present it">
    <Animal animal={animal} /> // <-- Error! expected Animal, but got Animal | undefined
  </If>
);

Benim yorumladığım gibi, aslında hayvan tanımlanmamış, daha önce kontrol ettiğim gibi Typescript'e söylemem mümkün değil. İddiası animal!işe yarayacaktır, ama aradığım şey bu değil.

Herhangi bir fikir?


1
daktiloda boş güvenliği zaten kontrol ettiğinizi söylemenin mümkün olup olmadığından emin değilim. Belki {animal !== undefined && <Anibal animal={animal} />}işe
yarar

1
Döküm işe yarıyor mu? <Animal animal={animal as Animal} />
paul

@ OlivierBoissé Bu sözdizimini kullanmakla kısıtlandım
Eliya Cohen

@paul evet, ancak "!" Sonunda?
Eliya Cohen

Yanıtlar:


3

İmkansız görünüyor.

Sebep: Biz değiştirirseniz Ifbileşenin içeriği dan

if (props.condition) {
  ...
}

tam tersi

if (!props.condition) {
  ...
}

Bu kez farkın ters yönde işlenmesini istediğinizi göreceksiniz.

  <If condition={animal === undefined} comment="if animal is defined, then present it">
    <Animal animal={animal} /> // <-- Error! expected Animal, but got Animal | undefined
  </If>

Bu, bağımsız olmadığı anlamına gelir, iki bileşenden herhangi birine dokunmadan bu tür bir türün bu durumda fark edilmesinin mümkün olmadığı sonucuna yol açar.


En iyi yaklaşımın ne olduğundan emin değilim, ama burada düşüncelerimden biri.

Sen tanımlayabiliriz Animal component'ın sahne animaltypescript en ile
Dağıtım koşullu türleri : nonnullable .

belge

type T34 = NonNullable<string | number | undefined>;  // string | number

kullanım

interface AnimalProps {
  // Before
  animal: Animal;
  // After
  animal: NonNullable<Animal>;
}

IfBileşenin durumu tarafından oluşturulmaz , ancak yalnızca child componentbu koşulun içini kullandığınız için , child component'aksesuarlarını none nullableşu şartlar altında ,olarak tasarlamak mantıklıdır.

türü Animaliçerir undefined.


Sorunumu nasıl çözdüğünden emin değilim. Hayvan bileşeninin If bileşeniyle hiçbir ilgisi yoktur. Kendini If bileşeniyle eşleşecek şekilde uyarlamamalıdır. Artı, NonNullable'ın sorunumla ilgili ne yapacağından emin değilim
Eliya Cohen

@EliyaCohen Güncellendi, bu cevap için bir şey eklemeniz gerekiyor mu?
keikai

Cevabınızı onayladım, ancak bir çözüm olmadığı için kabul edemiyorum. Muhtemelen gelecekte TS ve React'in böyle bir şeyi mümkün kılacağı zaman çözülecektir.
Eliya Cohen

1

Kısa cevap?

Yapamazsın.

animalOlarak tanımladığınız için Animal | undefined, kaldırmanın tek yolu undefinedbir koruma oluşturmak veya animalbaşka bir şey olarak yeniden oluşturmaktır . Condition özelliğinizde type guard'ı gizlediniz ve TypeScript'in orada neler olduğunu bilmesinin bir yolu yoktur, bu nedenle Animalve arasında seçim yaptığınızı bilemez undefined. Yapmanız veya kullanmanız gerekecek !.

Yine de düşünün: bu daha temiz hissedebilir, ancak belki de daha aşağıya doğru başka biri tarafından anlaşılması ve korunması gereken bir kod parçası oluşturur. Özünde, React bileşenlerinden oluşan ve TypeScript'e ek olarak birisinin öğrenmesi gereken yeni bir dil oluşturuyorsunuz.

Koşullu olarak JSX çıktısı almanın alternatif bir yöntemi render, koşullu içeriğinizi içeren değişkenleri tanımlamaktır .

render() {
  const conditionalComponent = condition ? <Component/> : null;

  return (
    <Zoo>
      { conditionalComponent }
    </Zoo>
  );
}

Bu, diğer geliştiricilerin anında tanıyacağı ve aramak zorunda kalmayacağı standart bir yaklaşımdır.


0

Bir geri çağrı kullanarak, return parametresinin boş bırakılamadığını yazabilirim.

Orijinal Ifbileşeninizi değiştiremiyorum çünkü ne olduğunu bilmiyorumcondition ve hangi değişkenin iddiacondition={animal !== undefined || true}

Ne yazık ki, IsDefinedbu davayı ele almak için yeni bir bileşen yaptım :

interface IsDefinedProps<T> {
  check: T;
  children: (defined: NonNullable<T>) => JSX.Element;
}

function nonNullable<T>(value: T): value is NonNullable<T> {
  return value !== undefined || value !== null;
}

function IsDefined({ children, check }: IsDefinedProps<T>) {
  return nonNullable(check) ? children(check) : null;
}

Belirten childrenaynı tip gibi olduğu T türü bir nonnullable geçilecek bir geri arama olacakcheck .

Bununla, bir null kontrol edilen değişken geçirilecek bir render geri çağrısı alırız.

  const aDefined: string | undefined = mapping.a;
  const bUndefined: string | undefined = mapping.b;

  return (
    <div className="App">
      <IsDefined check={aDefined}>
        {aDefined => <DoSomething message={aDefined} />} // is defined and renders
      </IsDefined>
      <IsDefined check={bUndefined}>
        {bUndefined => <DoSomething message={bUndefined} />} // is undefined and doesn't render
      </IsDefined>
    </div>
  );

Burada çalışan bir örneğim var https://codesandbox.io/s/blazing-pine-2t4sm


Bu güzel bir yaklaşım, ancak ne yazık ki Ifbileşen kullanma amacını yitirdi (böylece temiz görünebilir)
Eliya Cohen
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.