使用 TypeScript 的 React 组件中的默认值 属性
Default property value in React component using 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 组件都依赖于它们,不使用它们不是一个选择。
具有 class 组件的默认道具
使用static defaultProps
是正确的。您还应该为道具和状态使用接口,而不是 classes。
更新 2018/12/1:随着时间的推移,TypeScript 改进了与 defaultProps
相关的类型检查。继续阅读以了解最新和最重要的用法,直至旧用法和问题。
对于 TypeScript 3.0 及更高版本
TypeScript 专门 added support for 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" } />
注意:
foo
not 标记为可选(即 foo?: string
),即使它不是必需的 JSX 属性。标记为可选意味着它可以是 undefined
,但实际上它永远不会是 undefined
,因为 defaultProps
提供了默认值。把它想成类似于 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+ 以类似的方式对待 defaultProps
,这对 React 用户来说真的很酷!
defaultProps
没有明确的类型注释。它的类型由编译器推断和使用,以确定需要哪些 JSX 属性。您可以使用 defaultProps: Pick<PageProps, "foo">
来确保 defaultProps
匹配 PageProps
的子集。关于这个警告的更多信息是 explained here.
- 这需要
@types/react
版本 16.4.11
才能正常工作。
对于 TypeScript 2.1 到 3.0
在 TypeScript 3.0 实现对 defaultProps
的编译器支持之前,您仍然可以使用它,并且它在运行时 100% 与 React 一起工作,但由于 TypeScript 仅在检查 JSX 属性时才考虑道具使用 ?
将具有默认值的道具标记为可选。示例:
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
是个好主意,这样它可以根据你的 props 进行类型检查,但你不必为每个必需的 属性 提供默认值值,这是没有意义的,因为必需的属性永远不需要默认值。
- 当使用
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<>
并为所有必需的道具提供默认值(即使永远不会使用这些默认值)或完全省略显式类型注释。
默认道具 Functional Components
您也可以在函数组件上使用 defaultProps
,但您必须将函数键入 FunctionComponent
(StatelessComponent
in @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
了!请注意,如果您 do 在函数组件上提供 defaultProps
它将优先于默认参数值,因为 React 将始终显式传递 defaultProps
值(因此参数永远不会未定义,因此永远不会使用默认参数。)所以你会使用一个或另一个,而不是两个。
来自@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.
其实你可以使用 Typescript 的 interface inheritance。生成的代码只是有点冗长。
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+,使用 Partial < T > 而不是使您的界面属性可选。
export interface Props {
obj: Model,
a: boolean
b: boolean
}
public static defaultProps: Partial<Props> = {
a: true
};
Typescript 3.0 有一个 new solution 这个问题:
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
.
对于那些拥有需要默认值的可选道具的人。 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>
)
}
}
对于功能组件,我宁愿保留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>
)
}
使用 optional
和 default
功能组件的道具(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>
这里似乎有很多过度设计的答案...
只需使用内联三元...在此示例中,如果未提供框阴影,则默认值为 '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>
);
};
我不知道如何使用 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 组件都依赖于它们,不使用它们不是一个选择。
具有 class 组件的默认道具
使用static defaultProps
是正确的。您还应该为道具和状态使用接口,而不是 classes。
更新 2018/12/1:随着时间的推移,TypeScript 改进了与 defaultProps
相关的类型检查。继续阅读以了解最新和最重要的用法,直至旧用法和问题。
对于 TypeScript 3.0 及更高版本
TypeScript 专门 added support for 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" } />
注意:
foo
not 标记为可选(即foo?: string
),即使它不是必需的 JSX 属性。标记为可选意味着它可以是undefined
,但实际上它永远不会是undefined
,因为defaultProps
提供了默认值。把它想成类似于 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+ 以类似的方式对待defaultProps
,这对 React 用户来说真的很酷!defaultProps
没有明确的类型注释。它的类型由编译器推断和使用,以确定需要哪些 JSX 属性。您可以使用defaultProps: Pick<PageProps, "foo">
来确保defaultProps
匹配PageProps
的子集。关于这个警告的更多信息是 explained here.- 这需要
@types/react
版本16.4.11
才能正常工作。
对于 TypeScript 2.1 到 3.0
在 TypeScript 3.0 实现对 defaultProps
的编译器支持之前,您仍然可以使用它,并且它在运行时 100% 与 React 一起工作,但由于 TypeScript 仅在检查 JSX 属性时才考虑道具使用 ?
将具有默认值的道具标记为可选。示例:
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
是个好主意,这样它可以根据你的 props 进行类型检查,但你不必为每个必需的 属性 提供默认值值,这是没有意义的,因为必需的属性永远不需要默认值。 - 当使用
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<>
并为所有必需的道具提供默认值(即使永远不会使用这些默认值)或完全省略显式类型注释。
默认道具 Functional Components
您也可以在函数组件上使用 defaultProps
,但您必须将函数键入 FunctionComponent
(StatelessComponent
in @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
了!请注意,如果您 do 在函数组件上提供 defaultProps
它将优先于默认参数值,因为 React 将始终显式传递 defaultProps
值(因此参数永远不会未定义,因此永远不会使用默认参数。)所以你会使用一个或另一个,而不是两个。
来自@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.
其实你可以使用 Typescript 的 interface inheritance。生成的代码只是有点冗长。
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+,使用 Partial < T > 而不是使您的界面属性可选。
export interface Props {
obj: Model,
a: boolean
b: boolean
}
public static defaultProps: Partial<Props> = {
a: true
};
Typescript 3.0 有一个 new solution 这个问题:
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
.
对于那些拥有需要默认值的可选道具的人。 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>
)
}
}
对于功能组件,我宁愿保留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>
)
}
使用 optional
和 default
功能组件的道具(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>
这里似乎有很多过度设计的答案...
只需使用内联三元...在此示例中,如果未提供框阴影,则默认值为 '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>
);
};