Giá trị thuộc tính mặc định trong thành phần React bằng TypeScript


150

Tôi không thể tìm ra cách đặt giá trị thuộc tính mặc định cho các thành phần của mình bằng cách sử dụng Bản in.

Đây là mã nguồn:

class PageState
{
}

export class PageProps
{
    foo: string = "bar";
}

export class PageComponent extends React.Component<PageProps, PageState>
{
    public render(): JSX.Element
    {
        return (
            <span>Hello, world</span>
        );
    }
}

Và khi tôi cố gắng sử dụng thành phần như thế này:

ReactDOM.render(<PageComponent />, document.getElementById("page"));

Tôi nhận được một lỗi nói tài sản foobị thiếu. Tôi muốn sử dụng giá trị mặc định. Tôi cũng đã thử sử dụng static defaultProps = ...bên trong thành phần, nhưng nó không có tác dụng như tôi nghi ngờ.

src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.

Làm thế nào tôi có thể sử dụng các giá trị thuộc tính mặc định? Nhiều thành phần JS mà công ty tôi sử dụng dựa vào chúng và không sử dụng chúng không phải là một lựa chọn.


static defaultPropsđúng. Bạn có thể gửi mã đó?
Aaron Beall

Câu trả lời:


324

Đạo cụ mặc định với thành phần lớp

Sử dụng static defaultPropslà chính xác. Bạn cũng nên sử dụng các giao diện, không phải các lớp, cho các đạo cụ và trạng thái.

Cập nhật 2018/12/1 : TypeScript đã cải thiện việc kiểm tra loại liên quan đến defaultPropstheo thời gian. Đọc về cách sử dụng mới nhất và lớn nhất cho đến các vấn đề và cách sử dụng cũ hơn.

Dành cho TypeScript 3.0 trở lên

TypeScript đặc biệt bổ sung hỗ trợdefaultProps để thực hiện công việc kiểm tra kiểu như bạn mong đợi. Thí dụ:

interface PageProps {
  foo: string;
  bar: string;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, { this.props.foo.toUpperCase() }</span>
        );
    }
}

Mà có thể được kết xuất và biên dịch mà không cần thông qua một foothuộc tính:

<PageComponent bar={ "hello" } />

Lưu ý rằng:

Đối với TypeScript 2.1 cho đến 3.0

Trước khi TypeScript 3.0 triển khai hỗ trợ trình biên dịch, defaultPropsbạn vẫn có thể sử dụng nó và nó hoạt động 100% với React khi chạy, nhưng vì TypeScript chỉ xem xét các đạo cụ khi kiểm tra các thuộc tính JSX mà bạn phải đánh dấu các đạo cụ có mặc định là tùy chọn ?. Thí dụ:

interface PageProps {
    foo?: string;
    bar: number;
}

export class PageComponent extends React.Component<PageProps, {}> {
    public static defaultProps: Partial<PageProps> = {
        foo: "default"
    };

    public render(): JSX.Element {
        return (
            <span>Hello, world</span>
        );
    }
}

Lưu ý rằng:

  • Đó là một ý tưởng tốt để chú thích defaultPropsvới Partial<>để nó gõ-kiểm tra chống lại đạo cụ của bạn, nhưng bạn không cần phải cung cấp tất cả các tài sản cần thiết với giá trị mặc định, mà làm cho không có ý nghĩa vì các đặc tính cần thiết không bao giờ nên cần một mặc định.
  • Khi sử dụng strictNullChecksgiá trị của this.props.foosẽ là possibly undefinedvà yêu cầu một xác nhận không null (nghĩa là this.props.foo!) hoặc loại bảo vệ (tức là if (this.props.foo) ...) để loại bỏ undefined. Điều này gây khó chịu vì giá trị prop mặc định có nghĩa là nó thực sự sẽ không bao giờ được xác định, nhưng TS không hiểu luồng này. Đó là một trong những lý do chính TS 3.0 bổ sung hỗ trợ rõ ràng cho defaultProps.

Trước TypeScript 2.1

Điều này hoạt động giống nhau nhưng bạn không có Partialloại, vì vậy chỉ cần bỏ qua Partial<>và cung cấp giá trị mặc định cho tất cả các đạo cụ cần thiết (mặc dù những mặc định đó sẽ không bao giờ được sử dụng) hoặc bỏ hoàn toàn chú thích loại rõ ràng.

Đạo cụ mặc định với các thành phần chức năng

Bạn có thể sử dụng defaultPropsvào các thành phần chức năng là tốt, nhưng bạn phải gõ chức năng của mình vào FunctionComponent( StatelessComponenttrong @types/reacttrước khi phiên bản 16.7.2) giao diện để nguyên cảo biết về defaultPropsvề chức năng:

interface PageProps {
  foo?: string;
  bar: number;
}

const PageComponent: FunctionComponent<PageProps> = (props) => {
  return (
    <span>Hello, {props.foo}, {props.bar}</span>
  );
};

PageComponent.defaultProps = {
  foo: "default"
};

Lưu ý rằng bạn không phải sử dụng Partial<PageProps>bất cứ nơi nào vì FunctionComponent.defaultPropsđã được chỉ định là một phần trong TS 2.1+.

Một lựa chọn tốt khác (đây là những gì tôi sử dụng) là để cấu trúc các propstham số của bạn và gán trực tiếp các giá trị mặc định:

const PageComponent: FunctionComponent<PageProps> = ({foo = "default", bar}) => {
  return (
    <span>Hello, {foo}, {bar}</span>
  );
};

Sau đó, bạn không cần defaultPropstất cả! Hãy lưu ý rằng nếu bạn làm cung cấp defaultPropstrên một thành phần chức năng nó sẽ được ưu tiên hơn các giá trị tham số mặc định, vì Phản ứng sẽ luôn luôn vượt qua một cách rõ ràng defaultPropscác giá trị (vì vậy các thông số không bao giờ undefined, do đó các thông số mặc định là không bao giờ được sử dụng.) Vì vậy, bạn muốn sử dụng cái này hay cái kia, không phải cả hai.


2
Lỗi có vẻ như bạn đang sử dụng <PageComponent>ở đâu đó mà không vượt qua được chỗ dựa foo. Bạn có thể làm cho nó tùy chọn bằng cách sử dụng foo?: stringtrong giao diện của bạn.
Aaron Beall

1
@Aaron Nhưng tsc sẽ đưa ra lỗi biên dịch, vì defaultProps không thực hiện giao diện PageProps. Bạn phải đặt tất cả các thuộc tính giao diện tùy chọn (xấu) hoặc chỉ định giá trị mặc định cho tất cả các trường bắt buộc (bản tóm tắt không cần thiết) hoặc tránh chỉ định loại trên defaultProps.
pamelus

1
@adrianmoisa Ý bạn là đạo cụ mặc định? Có, nó hoạt động nhưng cú pháp thì khác ... Tôi sẽ thêm một ví dụ vào câu trả lời của mình khi tôi quay lại máy tính của mình ...
Aaron Beall

1
@AdrianMoisa Cập nhật với ví dụ về thành phần chức năng
Aaron Beall

1
@Jared Có, nó cần phải public static defaultPropshoặc static defaultProps( publiclà mặc định) để mọi thứ (trình biên dịch và thời gian chạy React) hoạt động bình thường. Nó có thể hoạt động trong thời gian chạy private static defaultPropschỉ vì privatepublickhông tồn tại trong thời gian chạy, nhưng trình biên dịch sẽ không hoạt động chính xác.
Aaron Beall

18

Với Bản in 2.1+, hãy sử dụng <T> một phần thay vì đặt thuộc tính giao diện của bạn tùy chọn.

export interface Props {
    obj: Model,
    a: boolean
    b: boolean
}

public static defaultProps: Partial<Props> = {
    a: true
};

6

Với Typecript 3.0, có một giải pháp mới cho vấn đề này:

export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Type-checks! No type assertions needed!
let el = <Greet />

Lưu ý rằng để làm việc này, bạn cần một phiên bản mới @types/reacthơn 16.4.6. Nó hoạt động với 16.4.11.


Câu trả lời chính xác! Làm thế nào một người có thể xử lý: export interface Props { name?: string;}tên là một chỗ dựa tùy chọn ? Tôi tiếp tục nhận đượcTS2532 Object is possibly 'undefined'
Fydo

@Fydo Tôi không cần phải có giá trị mặc định cho một chỗ dựa tùy chọn, vì đây undefinedlà một loại giá trị mặc định tự động cho các đạo cụ đó. Bạn muốn undefinedđôi khi có thể chuyển vào dưới dạng giá trị rõ ràng, nhưng có một giá trị mặc định khác? Bạn đã thử export interface Props {name: string | undefined;}thay thế? Không thử điều đó, chỉ là một ý tưởng.
CorayThan

Thêm ?cũng giống như thêm |undefined. Tôi muốn tùy ý vượt qua trong chỗ dựa, và hãy defaultPropsxử lý trường hợp đó. Có vẻ như điều này là không thể trong TS3. Tôi sẽ chỉ cần sử dụng sợ hãi name!cú pháp kể từ khi tôi biết nó không bao giờ undefinedkhi defaultPropsđược thiết lập. Dù sao đi nữa cũng xin cám ơn!
Fydo

1
Ủng hộ vì đây là câu trả lời đúng ngay bây giờ! Cũng cập nhật câu trả lời được chấp nhận của tôi (bắt đầu trở thành một cuốn sách lịch sử) với tính năng mới này, và giải thích thêm một chút. :)
Aaron Beall

5

Đối với những người có đạo cụ tùy chọn cần giá trị mặc định. Tín dụng ở đây :)

interface Props {
  firstName: string;
  lastName?: string;
}

interface DefaultProps {
  lastName: string;
}

type PropsWithDefaults = Props & DefaultProps;

export class User extends React.Component<Props> {
  public static defaultProps: DefaultProps = {
    lastName: 'None',
  }

  public render () {
    const { firstName, lastName } = this.props as PropsWithDefaults;

    return (
      <div>{firstName} {lastName}</div>
    )
  }
}

3

Từ một bình luận của @pamelus về câu trả lời được chấp nhận:

Bạn phải đặt tất cả các thuộc tính giao diện tùy chọn (xấu) hoặc chỉ định giá trị mặc định cho tất cả các trường bắt buộc (bản tóm tắt không cần thiết) hoặc tránh chỉ định loại trên defaultProps.

Trên thực tế, bạn có thể sử dụng kế thừa giao diện của Typecript . Mã kết quả chỉ dài hơn một chút.

interface OptionalGoogleAdsProps {
    format?: string;
    className?: string;
    style?: any;
    scriptSrc?: string
}

interface GoogleAdsProps extends OptionalGoogleAdsProps {
    client: string;
    slot: string;
}


/**
 * Inspired by https://github.com/wonism/react-google-ads/blob/master/src/google-ads.js
 */
export default class GoogleAds extends React.Component<GoogleAdsProps, void> {
    public static defaultProps: OptionalGoogleAdsProps = {
        format: "auto",
        style: { display: 'block' },
        scriptSrc: "//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"
    };

0

Đối với thành phần chức năng, tôi thà giữ propsđối số, vì vậy đây là giải pháp của tôi:

interface Props {
  foo: string;
  bar?: number; 
}

// IMPORTANT!, defaultProps is of type {bar: number} rather than Partial<Props>!
const defaultProps = {
  bar: 1
}


// externalProps is of type Props
const FooComponent = exposedProps => {
  // props works like type Required<Props> now!
  const props = Object.assign(defaultProps, exposedProps);

  return ...
}

FooComponent.defaultProps = defaultProps;

0

Thành phần chức năng

Trên thực tế, đối với thành phần chức năng, cách thực hành tốt nhất như dưới đây, tôi tạo một thành phần Spinner mẫu:

import React from 'react';
import { ActivityIndicator } from 'react-native';
import { colors } from 'helpers/theme';
import type { FC } from 'types';

interface SpinnerProps {
  color?: string;
  size?: 'small' | 'large' | 1 | 0;
  animating?: boolean;
  hidesWhenStopped?: boolean;
}

const Spinner: FC<SpinnerProps> = ({
  color,
  size,
  animating,
  hidesWhenStopped,
}) => (
  <ActivityIndicator
    color={color}
    size={size}
    animating={animating}
    hidesWhenStopped={hidesWhenStopped}
  />
);

Spinner.defaultProps = {
  animating: true,
  color: colors.primary,
  hidesWhenStopped: true,
  size: 'small',
};

export default Spinner;
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.