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에서 null 안전을 이미 알고 있다고 말할 수 있는지 확실하지 않습니다. 아마 {animal !== undefined && <Anibal animal={animal} />}작동 할 것입니다
Olivier Boissé

1
캐스팅이 작동합니까? <Animal animal={animal as Animal} />
Paul

@ 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>

이것은 독립적이지 않기 때문에 두 구성 요소 중 하나를 건드리지 않고이 조건에서 이러한 유형의 차이를 만들 수 없다는 결론을 내립니다.


최선의 방법이 무엇인지 잘 모르겠지만 여기에 내 생각 중 하나가 있습니다.

typescript의 Distributive conditional types : NonNullable로Animal component props 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

짧은 답변?

당신은 할 수 없습니다.

당신이 정의한 이후 animalAnimal | undefined, 제거 할 수있는 유일한 방법은 undefined두 가드 또는 참 -_- 만드는 것입니다 animal다른 뭔가로합니다. condition 속성에 type guard가 숨겨져 있으며 TypeScript는 어떤 일이 일어나고 있는지 알 수 없으므로 Animaland 중에서 선택하는 것을 알 수 없습니다 undefined. 전송하거나을 사용해야 !합니다.

그러나 이것은 더 깨끗하게 느껴질 수 있지만 아마도 다른 사람이 이해하고 유지해야 할 코드 조각을 만듭니다. 본질적으로 React 구성 요소로 만든 새로운 언어를 작성하고 있으며 TypeScript 외에도 누군가가 배워야합니다.

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 형 Null을 전달 될 콜백 것이다 check.

이를 통해 렌더 콜백을 얻게되는데, 이는 null-checked 변수로 전달됩니다.

  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.