Default property value in React component using TypeScript

前端 未结 7 1009
温柔的废话
温柔的废话 2020-11-28 01:51

I can\'t figure out how to set default property values for my components using Typescript.

This is the source code:

class PageState
{
}

export class         


        
相关标签:
7条回答
  • 2020-11-28 02:01

    From a comment by @pamelus on the accepted answer:

    You either have to make all interface properties optional (bad) or specify default value also for all required fields (unnecessary boilerplate) or avoid specifying type on defaultProps.

    Actually you can use Typescript's interface inheritance. The resulting code is only a little bit more 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"
        };
    
    0 讨论(0)
  • 2020-11-28 02:03

    For those having optional props that need default values. Credit here :)

    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>
        )
      }
    }
    
    0 讨论(0)
  • 2020-11-28 02:04

    With Typescript 3.0 there is a new solution to this issue:

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

    Note that for this to work you need a newer version of @types/react than 16.4.6. It works with 16.4.11.

    0 讨论(0)
  • 2020-11-28 02:06

    For the functional component, I would rather keep the props argument, so here is my solution:

    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 讨论(0)
  • 2020-11-28 02:14

    Functional Component

    Actually, for functional component the best practice is like below, I create a sample Spinner component:

    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;
    
    0 讨论(0)
  • 2020-11-28 02:15

    Default props with class component

    Using static defaultProps is correct. You should also be using interfaces, not classes, for the props and state.

    Update 2018/12/1: TypeScript has improved the type-checking related to defaultProps over time. Read on for latest and greatest usage down to older usages and issues.

    For TypeScript 3.0 and up

    TypeScript specifically added support for defaultProps to make type-checking work how you'd expect. Example:

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

    Which can be rendered and compile without passing a foo attribute:

    <PageComponent bar={ "hello" } />
    

    Note that:

    • foo is not marked optional (ie foo?: string) even though it's not required as a JSX attribute. Marking as optional would mean that it could be undefined, but in fact it never will be undefined because defaultProps provides a default value. Think of it similar to how you can mark a function parameter optional, or with a default value, but not both, yet both mean the call doesn't need to specify a value. TypeScript 3.0+ treats defaultProps in a similar way, which is really cool for React users!
    • The defaultProps has no explicit type annotation. Its type is inferred and used by the compiler to determine which JSX attributes are required. You could use defaultProps: Pick<PageProps, "foo"> to ensure defaultProps matches a sub-set of PageProps. More on this caveat is explained here.
    • This requires @types/react version 16.4.11 to work properly.

    For TypeScript 2.1 until 3.0

    Before TypeScript 3.0 implemented compiler support for defaultProps you could still make use of it, and it worked 100% with React at runtime, but since TypeScript only considered props when checking for JSX attributes you'd have to mark props that have defaults as optional with ?. Example:

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

    Note that:

    • It's a good idea to annotate defaultProps with Partial<> so that it type-checks against your props, but you don't have to supply every required property with a default value, which makes no sense since required properties should never need a default.
    • When using strictNullChecks the value of this.props.foo will be possibly undefined and require a non-null assertion (ie this.props.foo!) or type-guard (ie if (this.props.foo) ...) to remove undefined. This is annoying since the default prop value means it actually will never be undefined, but TS didn't understand this flow. That's one of the main reasons TS 3.0 added explicit support for defaultProps.

    Before TypeScript 2.1

    This works the same but you don't have Partial types, so just omit Partial<> and either supply default values for all required props (even though those defaults will never be used) or omit the explicit type annotation completely.

    Default props with Functional Components

    You can use defaultProps on function components as well, but you have to type your function to the FunctionComponent (StatelessComponent in @types/react before version 16.7.2) interface so that TypeScript knows about defaultProps on the function:

    interface PageProps {
      foo?: string;
      bar: number;
    }
    
    const PageComponent: FunctionComponent<PageProps> = (props) => {
      return (
        <span>Hello, {props.foo}, {props.bar}</span>
      );
    };
    
    PageComponent.defaultProps = {
      foo: "default"
    };
    

    Note that you don't have to use Partial<PageProps> anywhere because FunctionComponent.defaultProps is already specified as a partial in TS 2.1+.

    Another nice alternative (this is what I use) is to destructure your props parameters and assign default values directly:

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

    Then you don't need the defaultProps at all! Be aware that if you do provide defaultProps on a function component it will take precedence over default parameter values, because React will always explicitly pass the defaultProps values (so the parameters are never undefined, thus the default parameter is never used.) So you'd use one or the other, not both.

    0 讨论(0)
提交回复
热议问题