Làm thế nào tôi có thể tạo ra một thành phần React nếu If hành động như một người thực sự nếu mà trong bản mô tả?


11

Tôi đã tạo một <If />thành phần chức năng đơn giản bằng 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;
}

Nó cho phép tôi viết một mã sạch hơn, chẳng hạn như:

render() {

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

Trong hầu hết các trường hợp, nó hoạt động tốt, nhưng khi tôi muốn kiểm tra ví dụ nếu một biến đã cho không được xác định và sau đó chuyển nó thành thuộc tính, nó sẽ trở thành một vấn đề. Tôi sẽ đưa ra một ví dụ:

Giả sử tôi có một thành phần được gọi là <Animal />có các Đạo cụ sau:

interface AnimalProps {
  animal: Animal;
}

và bây giờ tôi có một thành phần khác biểu hiện DOM sau:

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

Như tôi đã nhận xét, mặc dù trên thực tế động vật không được xác định, tôi không có cách nào để nói với Bản mô tả rằng tôi đã kiểm tra nó. Khẳng định animal!sẽ có hiệu quả, nhưng đó không phải là điều tôi đang tìm kiếm.

Có ý kiến ​​gì không?


1
không chắc chắn nếu có thể trong bản thảo để nói với anh ta rằng bạn đã kiểm tra sự an toàn không. Có lẽ {animal !== undefined && <Anibal animal={animal} />}sẽ hoạt động
Olivier Boissé

1
Liệu đúc nó hoạt động? <Animal animal={animal as Animal} />
paul

@ OlivierBoissé Tôi bị hạn chế sử dụng cú pháp đó
Eliya Cohen

@paul có, nhưng sẽ không giống như đặt "!" đến cuối cùng?
Eliya Cohen

Câu trả lời:


3

Có vẻ như không thể.

Lý do: nếu chúng tôi thay đổi nội dungIf của thành phần từ

if (props.condition) {
  ...
}

đối diện với nó

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

Bạn sẽ thấy rằng lần này bạn muốn loại diff được xử lý theo cách ngược lại.

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

Điều đó có nghĩa là nó không độc lập, dẫn đến kết luận rằng không thể tạo ra loại khác như vậy trong điều kiện này, mà không chạm vào một trong hai thành phần.


Tôi không chắc cách tiếp cận tốt nhất là gì, nhưng đây là một trong những suy nghĩ của tôi.

Bạn có thể định nghĩa Animal componentcác đạo cụ animalvới các loại
điều kiện Phân phối của bản thảo : Không thể xóa được .

Tài liệu

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

Sử dụng

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

Nó không được tạo ra bởi Ifđiều kiện của thành phần, nhưng vì bạn chỉ sử dụng child componentbên trong điều kiện đó, nên có ý nghĩa gì đó để thiết kế các child componentđạo cụ như là none nullabletrong điều kiện

loại Animalcó chứa undefined.


Tôi không chắc nó giải quyết vấn đề của tôi như thế nào. Thành phần động vật không có gì liên quan đến thành phần If. Nó không nên tự điều chỉnh để phù hợp với thành phần If. Ngoài ra, tôi không chắc chắn NonNullable có liên quan gì đến vấn đề của tôi
Eliya Cohen

@EliyaCohen Cập nhật, có gì cần phải thêm vào câu trả lời này không?
keikai

Tôi đã đưa ra câu trả lời của bạn, mặc dù tôi không thể chấp nhận vì đây không phải là một giải pháp. Có lẽ sẽ được giải quyết trong tương lai khi TS và React sẽ biến điều đó thành có thể.
Eliya Cohen

1

Câu trả lời ngắn?

Bạn không thể.

Vì bạn đã xác định animalAnimal | undefined, cách duy nhất để xóa undefinedlà tạo một bộ bảo vệ hoặc đúc lại animalnhư một thứ khác. Bạn đã ẩn bộ bảo vệ kiểu trong thuộc tính điều kiện của mình và TypeScript không có cách nào biết được chuyện gì đang xảy ra ở đó, vì vậy nó không thể biết bạn đang chọn giữa Animalundefined. Bạn sẽ cần phải cast nó hoặc sử dụng !.

Mặc dù vậy, hãy xem xét: điều này có thể cảm thấy sạch hơn, nhưng nó tạo ra một đoạn mã cần được hiểu và duy trì, có lẽ bởi một người khác ở xa hơn. Về bản chất, bạn đang tạo một ngôn ngữ mới, được tạo thành từ các thành phần React, mà ai đó sẽ cần phải học ngoài TypeScript.

Một phương thức thay thế cho đầu ra có điều kiện JSX là xác định các biến trong renderđó có chứa nội dung có điều kiện của bạn, chẳng hạn như

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

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

Đây là một cách tiếp cận tiêu chuẩn mà các nhà phát triển khác sẽ nhận ra ngay lập tức và không phải tìm kiếm.


0

Bằng cách sử dụng gọi lại kết xuất, tôi có thể nhập tham số trả về không thể rỗng.

Tôi không thể sửa đổi Ifthành phần ban đầu của bạn vì tôi không biết conditionxác nhận của bạn là gì và cũng là biến số được xác nhận, nghĩa làcondition={animal !== undefined || true}

Thật không may, tôi đã tạo ra một thành phần mới IsDefinedđể xử lý trường hợp này:

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

Chỉ ra rằng đó childrensẽ là một cuộc gọi lại, sẽ được thông qua Không thể loại được loại T cùng loại với check.

Với điều này, chúng ta sẽ nhận được một cuộc gọi lại kết xuất, sẽ được thông qua một biến kiểm tra 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>
  );

Tôi có một ví dụ hoạt động ở đây https://codesandbox.io/s/blazed-pine-2t4sm


Đó là một cách tiếp cận tốt, nhưng thật không may, nó đánh bại toàn bộ mục đích sử dụng Ifthành phần (để nó có thể trông sạch sẽ)
Eliya Cohen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.