简体   繁体   English

使用 TypeScript 的 React 组件中的默认属性值

[英]Default property value in React component using TypeScript

I can't figure out how to set default property values for my components using Typescript.我不知道如何使用 Typescript 为我的组件设置默认属性值。

This is the source code:这是源代码:

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

And when I try to use the component like this:当我尝试使用这样的组件时:

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

I get an error saying property foo is missing.我收到一条错误消息,提示缺少属性foo I want to use the default value.我想使用默认值。 I've also tried to use static defaultProps = ... inside the component, but it had no effect as I suspected.我也尝试在组件内使用static defaultProps = ... ,但它没有我怀疑的效果。

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

How can I use default property values?如何使用默认属性值? Many JS components my company uses rely on them and not using them is not a choice.我公司使用的许多 JS 组件都依赖于它们,不使用它们不是一种选择。

Default props with class component带有类组件的默认道具

Using static defaultProps is correct.使用static defaultProps是正确的。 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. 2018/12/1 更新:TypeScript 随着时间的推移改进了与defaultProps相关的类型检查。 Read on for latest and greatest usage down to older usages and issues.继续阅读最新和最有用的用法,直至旧用法和问题。

For TypeScript 3.0 and up对于 TypeScript 3.0 及更高版本

TypeScript specifically added support for defaultProps to make type-checking work how you'd expect. TypeScript 专门添加了对defaultProps的支持,以使类型检查按您的预期工作。 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:无需传递foo属性即可渲染和编译:

<PageComponent bar={ "hello" } />

Note that:注意:

  • foo is not marked optional (ie foo?: string ) even though it's not required as a JSX attribute. foo没有标记为可选(即foo?: string ),即使它不是 JSX 属性所必需的。 Marking as optional would mean that it could be undefined , but in fact it never will be undefined because defaultProps provides a default value.标记为可选意味着它可能是undefined ,但实际上它永远不会是undefined ,因为defaultProps提供了一个默认值。 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 .可以将其想象为类似于如何将函数参数标记为可选或使用默认值,但不能同时使用两者,但两者都意味着调用不需要指定 value TypeScript 3.0+ treats defaultProps in a similar way, which is really cool for React users! TypeScript 3.0+ 以类似的方式处理defaultProps ,这对于 React 用户来说真的很酷!
  • The defaultProps has no explicit type annotation. defaultProps没有明确的类型注释。 Its type is inferred and used by the compiler to determine which JSX attributes are required.编译器推断并使用它的类型来确定需要哪些 JSX 属性。 You could use defaultProps: Pick<PageProps, "foo"> to ensure defaultProps matches a sub-set of PageProps .您可以使用defaultProps: Pick<PageProps, "foo">来确保defaultProps匹配PageProps的子集。 More on this caveat is explained here . 此处解释了有关此警告的更多信息。
  • This requires @types/react version 16.4.11 to work properly.这需要@types/react版本16.4.11才能正常工作。

For TypeScript 2.1 until 3.0对于 TypeScript 2.1 到 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 ?在 TypeScript 3.0 实现对defaultProps的编译器支持之前,你仍然可以使用它,并且它在运行时与 React 100% 工作,但由于 TypeScript 在检查 JSX 属性时只考虑道具,你必须将具有默认值的道具标记为可选? . . 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.Partial<>注释defaultProps是个好主意,这样它就可以对你的 props 进行类型检查,但你不必为每个必需的属性提供默认值,这是没有意义的,因为必需的属性永远不需要默认值。
  • 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 .当使用strictNullChecksthis.props.foo的值possibly undefined的,并且需要一个非空断言(即this.props.foo! )或类型保护(即if (this.props.foo) ... )来删除undefined This is annoying since the default prop value means it actually will never be undefined, but TS didn't understand this flow.这很烦人,因为默认的 prop 值意味着它实际上永远不会未定义,但 TS 不理解这个流程。 That's one of the main reasons TS 3.0 added explicit support for defaultProps .这是 TS 3.0 添加对defaultProps的显式支持的主要原因之一。

Before TypeScript 2.1在 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.这工作相同,但您没有Partial类型,因此只需省略Partial<>并为所有必需的道具提供默认值(即使这些默认值永远不会被使用)或完全省略显式类型注释。

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:你也可以在函数组件上使用defaultProps ,但是你必须将你的函数输入到FunctionComponentStatelessComponent@types/react版本16.7.2之前)接口中,以便 TypeScript 知道函数上的defaultProps

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+.请注意,您不必在任何地方使用Partial<PageProps>因为FunctionComponent.defaultProps已在 TS 2.1+ 中指定为部分。

Another nice alternative (this is what I use) is to destructure your props parameters and assign default values directly:另一个不错的选择(这是我使用的)是解构你的props参数并直接分配默认值:

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

Then you don't need the defaultProps at all!那么你根本不需要defaultProps 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.请注意,如果您确实在函数组件上提供defaultProps ,它将优先于默认参数值,因为 React 将始终显式传递defaultProps值(因此参数永远不会未定义,因此永远不会使用默认参数。)所以你' d 使用其中之一,而不是两者都使用。

With Typescript 2.1+, use Partial < T > instead of making your interface properties optional.使用 Typescript 2.1+,使用Partial < T >而不是让你的界面属性可选。

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

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

You can use the spread operator to re-assign props with a standard functional component.您可以使用扩展运算符重新分配具有标准功能组件的道具。 The thing I like about this approach is that you can mix required props with optional ones that have a default value.我喜欢这种方法的一点是,您可以将必需的道具与具有默认值的可选道具混合使用。

interface MyProps {
   text: string;
   optionalText?: string;
}

const defaultProps = {
   optionalText = "foo";
}

const MyComponent = (props: MyProps) => {
   props = { ...defaultProps, ...props }
}

With Typescript 3.0 there is a new solution to this issue: Typescript 3.0 有一个新的解决方案来解决这个问题:

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 .请注意,要使其正常工作,您需要比16.4.6更新版本的@types/react It works with 16.4.11 .它适用于16.4.11

Functional Component功能组件

Actually, for functional component the best practice is like below, I create a sample Spinner component:实际上,对于功能组件,最佳实践如下所示,我创建了一个示例 Spinner 组件:

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

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

const Spinner = ({
  color = colors.primary,
  size = 'small',
  animating = true,
  hidesWhenStopped = true,
}: SpinnerProps): JSX.Element => (
  <ActivityIndicator
    color={color}
    size={size}
    animating={animating}
    hidesWhenStopped={hidesWhenStopped}
  />
);

export default Spinner;

If your component has children it would be better to use React.FC , like following:如果您的组件有children组件,最好使用React.FC ,如下所示:

export interface TypographyProps {
  color?: string;
}

const Typography: React.FC<TypographyProps> = ({
  children,
  color,
}) => (
  <span style={{ color }}>
    {children}
  </span>
);

export default Typography;

From a comment by @pamelus on the accepted answer:来自@pamelus 对已接受答案的评论:

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.您要么必须将所有接口属性设为可选(不好),要么为所有必填字段(不必要的样板)指定默认值,或者避免在 defaultProps 上指定类型。

Actually you can use Typescript's interface inheritance .实际上你可以使用 Typescript 的接口继承 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"
    };

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

For the functional component, I would rather keep the props argument, so here is my solution:对于功能组件,我宁愿保留props参数,所以这是我的解决方案:

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;

With optional and default props for functional component (Typescript 4.4+):使用功能组件的optionaldefault道具(Typescript 4.4+):

export const LoadingSpinner = ({
  size = "lg",
  children,
}: {
  size?: "sm" | "base" | "lg";
  children?: any;
}) => {
console.log(size);
return <div>{children}</div>
};

use it like this:像这样使用它:

 <LoadingSpinner size="sm"><p>hello</p></LoadingSpinner>
 <LoadingSpinner><p>hello</p></LoadingSpinner>

Hooks (with Typescript)钩子(带打字稿)

export interface ApprovalRejectModalProps{
 singleFileApprove:boolean;
}

ApproveRejectModal.defaultProps={
 singleFileApprove:false --> default value
}

export const ApproveRejectModal:React.FC<ApprovalRejectModalProps>=(props)=>{
return (
        <div>
            ....
        </div>
       )
}

Seems to be a lot of over engineered answers here...这里似乎有很多过度设计的答案......

Just use an inline ternary... in this example if no box shadow is supplied then the default value is 'none' , if a value is supplied then it is that value只需使用内联三元...在此示例中,如果未提供框阴影,则默认值为 'none' ,如果提供了值,则为该值

export interface FormItemProps {
  boxShadow?: boolean;
  marginBottom: string;
}

export const FormItem = (props: FormItemProps) => {
  return (
    <div
      style={{marginBottom: props.marginBottom,}}
    >
     
      <div            
        style={{boxShadow: props.boxShadow ? props.boxShadow : 'none',}}
      >
        {'some text..... '}
      </div>
    </div>
  );
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM