Nilai properti default di komponen Bereaksi menggunakan TypeScript

153

Saya tidak tahu bagaimana cara menetapkan nilai properti default untuk komponen saya menggunakan Typecript.

Ini adalah kode sumber:

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

Dan ketika saya mencoba menggunakan komponen seperti ini:

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

Saya mendapatkan pesan kesalahan yang mengatakan bahwa properti footidak ada. Saya ingin menggunakan nilai default. Saya juga mencoba menggunakan static defaultProps = ...di dalam komponen, tetapi tidak memiliki efek seperti yang saya duga.

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

Bagaimana saya bisa menggunakan nilai properti default? Banyak komponen JS yang digunakan perusahaan saya bergantung padanya dan tidak menggunakannya bukanlah pilihan.

Tom
sumber
static defaultPropsbenar. Bisakah Anda memposting kode itu?
Aaron Beall

Jawaban:

327

Alat peraga bawaan dengan komponen kelas

Penggunaannya static defaultPropsbenar. Anda juga harus menggunakan antarmuka, bukan kelas, untuk alat peraga dan negara.

Pembaruan 2018/12/1 : TypeScript telah meningkatkan pemeriksaan tipe yang terkait dengan defaultPropswaktu. Baca terus untuk penggunaan terbaru dan terhebat hingga penggunaan dan masalah yang lebih lama.

Untuk TypeScript 3.0 dan lebih tinggi

TypeScript secara khusus menambahkan dukungan untukdefaultProps membuat pemeriksaan jenis berfungsi seperti yang Anda harapkan. Contoh:

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

Yang dapat dirender dan dikompilasi tanpa melewati fooatribut:

<PageComponent bar={ "hello" } />

Perhatikan bahwa:

  • fooadalah tidak ditandai opsional (yaitu foo?: string) meskipun itu tidak diperlukan sebagai atribut BEJ. Menandai sebagai opsional berarti bahwa itu bisa saja undefined, tetapi sebenarnya tidak akan pernah undefinedkarena defaultPropsmemberikan nilai default. Anggap saja mirip dengan bagaimana Anda dapat menandai parameter fungsi opsional, atau dengan nilai default, tetapi tidak keduanya, namun keduanya berarti panggilan tidak perlu menentukan nilai . TypeScript 3.0+ memperlakukan defaultPropsdengan cara yang serupa, yang sangat keren untuk pengguna Bereaksi!
  • Tidak defaultPropsmemiliki anotasi jenis eksplisit. Jenisnya disimpulkan dan digunakan oleh kompiler untuk menentukan atribut JSX mana yang diperlukan. Anda bisa menggunakan defaultProps: Pick<PageProps, "foo">untuk memastikan defaultPropskecocokan sub-set dari PageProps. Lebih lanjut tentang peringatan ini dijelaskan di sini .
  • Ini membutuhkan @types/reactversi 16.4.11untuk berfungsi dengan baik.

Untuk TypeScript 2.1 hingga 3.0

Sebelum TypeScript 3.0 menerapkan dukungan kompiler untuk defaultPropsAnda masih bisa memanfaatkannya, dan itu bekerja 100% dengan Bereaksi pada saat runtime, tetapi karena TypeScript hanya mempertimbangkan alat peraga ketika memeriksa atribut JSX Anda harus menandai alat peraga yang memiliki standar sebagai opsional dengan ?. Contoh:

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

Perhatikan bahwa:

  • Ini adalah ide yang baik untuk membuat catatan defaultPropsdengan itu Partial<>sehingga ketik-cek terhadap alat peraga Anda, tetapi Anda tidak harus menyediakan setiap properti yang diperlukan dengan nilai default, yang tidak masuk akal karena properti yang diperlukan seharusnya tidak pernah membutuhkan default.
  • Saat menggunakan strictNullChecksnilai this.props.fooakan possibly undefineddan membutuhkan pernyataan non-nol (yaitu this.props.foo!) atau tipe-penjaga (yaitu if (this.props.foo) ...) untuk menghapus undefined. Ini menjengkelkan karena nilai prop standar berarti sebenarnya tidak akan pernah dapat ditentukan, tetapi TS tidak memahami aliran ini. Itulah salah satu alasan utama TS 3.0 menambahkan dukungan eksplisit untuk defaultProps.

Sebelum TypeScript 2.1

Ini berfungsi sama tetapi Anda tidak memiliki Partialjenis, jadi abaikan saja Partial<>dan berikan nilai standar untuk semua alat peraga yang diperlukan (meskipun standar itu tidak akan pernah digunakan) atau abaikan anotasi jenis eksplisit sepenuhnya.

Alat peraga bawaan dengan Komponen Fungsional

Anda dapat menggunakan defaultPropskomponen fungsi juga, tetapi Anda harus mengetikkan fungsi Anda ke antarmuka FunctionComponent( StatelessComponentdalam @types/reactversi sebelum 16.7.2) sehingga TypeScript tahu tentang defaultPropsfungsi:

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

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

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

Perhatikan bahwa Anda tidak harus menggunakan di Partial<PageProps>mana pun karena FunctionComponent.defaultPropssudah ditentukan sebagai parsial di TS 2.1+.

Alternatif lain yang bagus (ini yang saya gunakan) adalah merusak propsparameter Anda dan menetapkan nilai default secara langsung:

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

Maka Anda tidak perlu defaultPropssama sekali! Sadarilah bahwa jika Anda tidak memberikan defaultPropspada komponen fungsi itu akan lebih diutamakan daripada nilai parameter default, karena Bereaksi akan selalu secara eksplisit lulus defaultPropsnilai (sehingga parameter tidak pernah terdefinisi, sehingga parameter default tidak pernah digunakan.) Jadi, Anda akan menggunakan satu atau yang lain, tidak keduanya.

Aaron Beall
sumber
2
Kesalahan terdengar seperti Anda menggunakan <PageComponent>suatu tempat tanpa melewati fooprop. Anda dapat membuatnya opsional menggunakan foo?: stringdi antarmuka Anda.
Aaron Beall
1
@ Harun Tapi tsc akan membuang kesalahan kompilasi, karena defaultProps tidak mengimplementasikan antarmuka PageProps. Anda harus membuat semua properti antarmuka opsional (buruk) atau menentukan nilai default juga untuk semua bidang yang diperlukan (boilerplate yang tidak perlu) atau menghindari menentukan jenis pada defaultProps.
pamelus
1
@adrianmoisa Maksud Anda alat peraga default? Ya itu berfungsi tetapi sintaksnya berbeda ... Saya akan menambahkan contoh untuk jawaban saya ketika saya kembali ke komputer saya ...
Aaron Beall
1
@AdrianMoisa Diperbarui dengan komponen contoh fungsi
Aaron Beall
1
@ Jared Ya, harus public static defaultPropsatau static defaultProps( publicdefault) untuk semuanya (compiler dan React runtime) agar berfungsi dengan baik. Ini dapat bekerja pada saat runtime private static defaultPropshanya karena privatedan publictidak ada saat runtime, tetapi kompiler tidak akan berfungsi dengan benar.
Aaron Beall
18

Dengan Typescript 2.1+, gunakan Partial <T> alih-alih menjadikan properti antarmuka Anda opsional.

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

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

Dengan Typescript 3.0 ada solusi baru untuk masalah ini:

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

Perhatikan bahwa untuk bekerja Anda memerlukan versi terbaru dari @types/reactdari 16.4.6. Ini bekerja dengan 16.4.11.

CorayThan
sumber
Jawaban bagus! Bagaimana mungkin orang menangani: di export interface Props { name?: string;}mana nama adalah properti opsional ? Saya terus mendapatkanTS2532 Object is possibly 'undefined'
Fydo
@ Fydo Saya tidak perlu memiliki nilai default untuk prop opsional, karena undefinedmerupakan semacam nilai default otomatis untuk props tersebut. Anda ingin dapat masuk undefinedsebagai nilai eksplisit kadang-kadang, tetapi memiliki nilai default lain? Sudahkah Anda mencoba export interface Props {name: string | undefined;}? Belum mencobanya, hanya sebuah ide.
CorayThan
Menambahkan ?sama dengan menambahkan |undefined. Saya ingin melewati prop, dan biarkan defaultPropsmenangani kasus itu. Sepertinya ini belum mungkin di TS3. Saya hanya akan menggunakan name!sintaks yang ditakuti karena saya tahu itu tidak pernah undefinedketika defaultPropsdiatur. Bagaimanapun, terima kasih!
Fydo
1
Terpilih karena ini adalah jawaban yang tepat sekarang! Juga memperbarui jawaban saya yang diterima (yang mulai menjadi buku sejarah) dengan fitur baru ini, dan sedikit penjelasan lagi. :)
Aaron Beall
5

Bagi mereka yang memiliki alat peraga opsional yang membutuhkan nilai default. Kredit di sini :)

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>
    )
  }
}
Morlo Mbakop
sumber
3

Dari komentar oleh @pamelus pada jawaban yang diterima:

Anda harus membuat semua properti antarmuka opsional (buruk) atau menentukan nilai default juga untuk semua bidang yang diperlukan (boilerplate yang tidak perlu) atau menghindari menentukan jenis pada defaultProps.

Sebenarnya Anda bisa menggunakan TypeScript pewarisan antarmuka . Kode yang dihasilkan hanya sedikit lebih verbose.

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"
    };
Harald Mühlhoff
sumber
0

Untuk komponen fungsional, saya lebih suka menyimpan propsargumen, jadi inilah solusi saya:

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;
xiechao06
sumber
0

Komponen Fungsional

Sebenarnya, untuk komponen fungsional praktik terbaiknya adalah seperti di bawah ini, saya membuat sampel komponen Spinner:

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;
Amerika
sumber