Bagaimana saya bisa membuat komponen "Jika" Bereaksi yang bertindak seperti "jika" yang nyata dalam naskah?

11

Saya membuat <If />komponen fungsi sederhana menggunakan React:

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;
}

Ini memungkinkan saya menulis kode pembersih, seperti:

render() {

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

Dalam kebanyakan kasus, ini berfungsi dengan baik, tetapi ketika saya ingin memeriksa misalnya jika variabel yang diberikan tidak didefinisikan dan kemudian meneruskannya sebagai properti, itu menjadi masalah. Saya akan memberi contoh:

Katakanlah saya memiliki komponen bernama <Animal />yang memiliki Alat Peraga berikut:

interface AnimalProps {
  animal: Animal;
}

dan sekarang saya memiliki komponen lain yang membuat DOM berikut:

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

Seperti yang saya komentari, walaupun sebenarnya hewan tidak didefinisikan, saya tidak punya cara untuk mengatakan bahwa saya sudah memeriksanya. Pernyataan animal!akan berhasil, tapi bukan itu yang saya cari.

Ada ide?

Eliya Cohen
sumber
1
tidak yakin apakah itu mungkin dalam naskah untuk mengatakan kepadanya Anda sudah chech null safety. Mungkin {animal !== undefined && <Anibal animal={animal} />}akan berhasil
Olivier Boissé
1
Apakah casting berhasil? <Animal animal={animal as Animal} />
paul
@ OlivierBoissé Saya dibatasi menggunakan sintaksis itu
Eliya Cohen
@ paul ya, tapi bukankah mirip dengan menaruh "!" pada akhirnya?
Eliya Cohen

Jawaban:

3

Sepertinya tidak mungkin.

Alasan: jika kami mengubah kontenIf komponen dari

if (props.condition) {
  ...
}

sebaliknya

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

Anda akan menemukan bahwa kali ini Anda ingin jenis diff diproses dengan cara yang berlawanan.

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

Yang berarti itu tidak independen, mengarah pada kesimpulan bahwa tidak mungkin untuk membuat tipe seperti itu berbeda dalam kondisi ini, tanpa menyentuh salah satu dari dua komponen.


Saya tidak yakin apa pendekatan terbaik, tapi ini salah satu pemikiran saya.

Anda dapat mendefinisikan Animal componentalat peraga animaldengan
tipe kondisional distributif naskah : NonNullable .

Dokumen

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

Pemakaian

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

Itu tidak dihasilkan oleh kondisi Ifkomponen, tetapi karena Anda hanya menggunakan bagian child componentdalam kondisi itu, masuk akal untuk merancang child componentalat peraga sebagai none nullable, dengan syarat bahwa

ketik Animalmengandung undefined.

keikai
sumber
Saya tidak yakin bagaimana ini menyelesaikan masalah saya. Komponen hewan tidak ada hubungannya dengan komponen If. Seharusnya tidak menyesuaikan diri agar sesuai dengan komponen Jika. Plus, saya tidak yakin bagaimana NonNullable ada hubungannya dengan masalah saya
Eliya Cohen
@ EliyaCohen Diperbarui, ada yang perlu ditambahkan untuk jawaban ini?
keikai
Saya telah menyetujui jawaban Anda, meskipun saya tidak dapat menerimanya karena itu bukan solusi. Mungkin akan terselesaikan di masa depan ketika TS dan Bereaksi akan membuat hal seperti itu menjadi mungkin.
Eliya Cohen
1

Jawaban singkat?

Kamu tidak bisa

Karena Anda telah menetapkan animalsebagai Animal | undefined, satu-satunya cara untuk menghapus undefinedadalah membuat penjaga atau menyusun kembali animalsebagai sesuatu yang lain. Anda telah menyembunyikan penjaga tipe di properti kondisi Anda, dan TypeScript tidak memiliki cara untuk mengetahui apa yang terjadi di sana, sehingga tidak dapat mengetahui Anda memilih di antara Animaldan undefined. Anda harus menggunakan atau menggunakannya !.

Pertimbangkan, meskipun: ini mungkin terasa lebih bersih, tetapi itu menciptakan sepotong kode yang perlu dipahami dan dipelihara, mungkin oleh orang lain di ujung tanduk. Intinya Anda membuat bahasa baru, terbuat dari komponen Bereaksi, yang perlu dipelajari seseorang selain TypeScript.

Metode alternatif untuk menghasilkan JSX secara kondisional adalah dengan mendefinisikan variabel renderyang berisi konten bersyarat Anda, seperti

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

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

Ini adalah pendekatan standar yang pengembang lain akan langsung mengenali dan tidak perlu melihat ke atas.

Michael Landis
sumber
0

Dengan menggunakan panggilan balik render, saya dapat mengetik bahwa parameter pengembalian tidak dapat dibatalkan.

Saya tidak dapat mengubah Ifkomponen asli Anda karena saya tidak tahu apa yang Anda conditiontegaskan dan juga variabel apa yang dinyatakannyacondition={animal !== undefined || true}

Sayangnya, saya membuat komponen baru IsDefineduntuk menangani kasus ini:

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;
}

Menunjukkan bahwa childrenakan menjadi callback, yang akan melewati NonNullable dari tipe T yang merupakan tipe yang sama check.

Dengan ini, kita akan mendapatkan panggilan balik render, yang akan melewati variabel nol-diperiksa.

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

Saya punya contoh yang berfungsi di sini https://codesandbox.io/s/blazing-pine-2t4sm

pengguna2340824
sumber
Itu pendekatan yang bagus, tetapi sayangnya itu mengalahkan seluruh tujuan menggunakan Ifkomponen (sehingga bisa terlihat bersih)
Eliya Cohen