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

我不知道如何使用Typescript为我的组件设置默认属性值。

这是源代码:

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

当我尝试像这样使用组件时:

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

我得到一个错误,说属性foo缺失。我想使用默认值。我还尝试在组件中使用static defaultProps = ...,但它没有我所怀疑的效果。

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

如何使用默认属性值?我的公司使用的许多JS组件都依赖于它们,不使用它们是不可取的。

189437 次浏览

默认道具与类组件

使用static defaultProps是正确的。对于道具和状态,还应该使用接口,而不是类。

< em >更新2018/12/1:随着时间的推移,TypeScript已经改进了与defaultProps相关的类型检查。。

适用于TypeScript 3.0及以上版本

TypeScript特别增加了对defaultProps的支持使类型检查按您期望的方式工作。例子:

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

可以在不传递foo属性的情况下呈现和编译:

<PageComponent bar={ "hello" } />

注意:

对于TypeScript 2.1到3.0

在TypeScript 3.0实现编译器对defaultProps的支持之前,你仍然可以使用它,它在运行时与React一起100%工作,但由于TypeScript在检查JSX属性时只考虑props,所以你必须用?将默认的props标记为可选。例子:

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

注意:

  • Partial<>注释defaultProps是个好主意,这样它就可以根据你的道具进行类型检查,但你不必为每个必需属性提供默认值,这没有意义,因为必需属性不应该需要默认值。
  • 当使用strictNullChecks时,this.props.foo的值将是possibly undefined,并且需要一个非空断言(即this.props.foo!)或类型保护(即if (this.props.foo) ...)来删除undefined。这很烦人,因为默认的prop值意味着它实际上永远不会是未定义的,但是TS不理解这个流程。这是TS 3.0为defaultProps添加显式支持的主要原因之一。

在TypeScript 2.1之前

这是一样的,但你没有Partial类型,所以只要省略Partial<>并为所有必需的道具提供默认值(即使这些默认值永远不会被使用)或完全省略显式类型注释。

功能组件的默认道具

你也可以在函数组件上使用defaultProps,但你必须将你的函数输入到FunctionComponent (StatelessComponent@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"
};

注意,你不必在任何地方使用Partial<PageProps>,因为FunctionComponent.defaultProps在TS 2.1+中已经被指定为一个分部。

另一个不错的选择(这是我使用的)是解构你的props参数,并直接分配默认值:

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

那么你根本不需要defaultProps !注意,如果在函数组件上提供了defaultProps,它将优先于默认参数值,因为React总是显式地传递defaultProps值(因此参数永远不会是undefined,因此默认参数永远不会被使用)。所以你只能用其中一个,而不是两个。

来自@pamelus对公认答案的评论:

你要么必须使所有接口属性都是可选的(坏的),要么 还为所有必选字段指定默认值(不需要)

. boilerplate)或避免指定defaultProps的类型

实际上你可以使用Typescript的接口继承。结果代码只是稍微啰嗦一点。

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

在Typescript 2.1+中,使用部分& lt;T >来代替可选的接口属性。

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


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

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

注意,要实现这一点,你需要一个比16.4.6更新的@types/react版本。它适用于16.4.11

对于那些需要默认值的可选道具。图片来源:

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

对于函数组件,我宁愿保留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;

功能组件

实际上,对于功能性组件的最佳实践如下所示,我创建了一个示例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;

如果你的组件有children,最好使用React.FC,如下所示:

export interface TypographyProps {
color?: string;
}


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


export default Typography;

您可以使用展开运算符使用标准功能组件重新分配道具。我喜欢这种方法的一点是,您可以将必需的道具与具有默认值的可选道具混合使用。

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


const defaultProps = {
optionalText = "foo";
}


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

钩子(带有Typescript)

export interface ApprovalRejectModalProps{
singleFileApprove:boolean;
}


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


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

函数组件的optionaldefault道具(Typescript 4.4+):

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

像这样使用它:

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


看看我的解决方案:

interface Props {
foo?: string | undefined;
bar: string;
other?: string;
}


export const Component = ({foo, bar, other = 'default text'}:Props) => {
console.log(foo, bar, other);
return(
...//some cool stuff your component should do
)
}


<Component bar='obligatory text'/>