Typescriptで実際の「if」のように動作するReact「If」コンポーネントを作成するにはどうすればよいですか?


11

シンプルにした <If />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;
}

次のようなよりクリーンなコードを記述できます。

render() {

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

ほとんどの場合、問題なく機能しますが、たとえば、指定された変数が定義されていないかどうかを確認してから、それをプロパティとして渡したい場合は、問題になります。例を挙げましょう:

<Animal />次の小道具を持つコンポーネントが呼び出されたとしましょう:

interface AnimalProps {
  animal: Animal;
}

そして今、私は次のDOMをレンダリングする別のコンポーネントを持っています:

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

私がコメントしたように、実際には動物は定義されていませんが、Typescriptにチェック済みであることを伝える方法はありません。の主張animal!はうまくいくでしょうが、それは私が探しているものではありません。

何か案は?


1
typescriptでヌルセーフティをすでにチェックしていることを彼に伝えることが可能かどうかはわかりません。多分{animal !== undefined && <Anibal animal={animal} />}
うまくいく

1
キャストは機能しますか?<Animal animal={animal as Animal} />
ポール

@OlivierBoisséその構文の使用は制限されています
Eliya Cohen

@paulはい、しかし "!"を置くのと同じではないでしょうか 最終的には?
エリヤコーエン

回答:


3

それは不可能のようです。

理由:Ifコンポーネントのコンテンツ

if (props.condition) {
  ...
}

反対です

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

今回は、タイプdiffが逆の方法で処理されることを確認します。

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

これは独立していないことを意味し、2つのコンポーネントのいずれにも触れずにこの状態でそのような型をdiffすることは不可能であるという結論につながります。


何が最善のアプローチかわかりませんが、ここに私の考えの1つがあります。

typescriptの配布条件タイプNonNullableを使用して、Animal component小道具animalを定義できます。

資料

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

使用法

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

これはIfコンポーネントの条件によって生成されるのではありませんが、child componentその条件の内部のみを使用するため、次の条件の下でchild componentのプロップをとして設計することは理にかなってnone nullableいます

タイプAnimalは含むundefined


それが私の問題をどのように解決するのかわかりません。動物コンポーネントは、Ifコンポーネントに関連するものは何もありません。それ自体がIfコンポーネントと一致するように適応すべきではありません。さらに、NonNullableが私の問題にどのように関係しているかわかりません
Eliya Cohen

@EliyaCohen更新、この回答に何か追加する必要がある?
keikai

あなたの答えを承認しましたが、それは解決策ではないので受け入れられません。TSとReactがそのようなことを可能にする将来的に解決されるでしょう。
エリヤコーエン

1

簡潔な答え?

できません。

animalとして定義したのでAnimal | undefined、削除する唯一の方法undefinedは、ガードを作成するか、animal別のものとしてリキャストすることです。あなたはあなたの状態プロパティに型ガードを隠されてきた、そしてそれはあなたがの間で選択されて知ることができないので、活字体は、そこに何が起こっているか知る方法がないAnimalundefined。キャストまたは使用する必要があります!

ただし、これはよりクリーンに感じるかもしれませんが、おそらく他の誰かが理解して保守する必要のあるコードを作成します。つまり、TypeScriptに加えて誰かが学ぶ必要のあるReactコンポーネントでできた新しい言語を作成しています。

JSXを条件付きで出力する別の方法はrender、次のような条件付きコンテンツを含む変数を定義することです。

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

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

これは、他の開発者が即座に認識し、調べる必要がない標準的なアプローチです。


0

レンダーコールバックを使用することで、戻りパラメーターをnullにできないことを入力できます。

元のIfコンポーネントを変更することはできません。conditionアサートの内容とアサートされた変数がcondition={animal !== undefined || true}

残念ながら、IsDefinedこのケースを処理するための新しいコンポーネントを作成しました。

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

それchildrenがコールバックであることを示し、それはと同じタイプであるタイプTのNonNullableを渡されますcheck

これにより、nullチェックされた変数が渡されるレンダーコールバックが取得されます。

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

ここに実際の例がありますhttps://codesandbox.io/s/blazing-pine-2t4sm


これは良いアプローチですが、残念ながら、Ifコンポーネントを使用する目的全体を無効にします(そのため、見た目がきれいになる可能性があります)。
Eliya Cohen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.