React 中的默认 Props

通过组件的 defaultProps 属性可为其 Props 指定默认值。

以下示例来自 React 官方文档 - Default Prop Values:

class Greeting extends React.Component {render() {return (<h1>Hello, {this.props.name}</h1>);}
}// Specifies the default values for props:
Greeting.defaultProps = {name: 'Stranger'
};// Renders "Hello, Stranger":
ReactDOM.render(<Greeting />,document.getElementById('example')
);

如果编译过程使用了 Babel 的 transform-class-properties 插件,还可以这么写:

class Greeting extends React.Component {static defaultProps = {name: 'stranger'}render() {return (<div>Hello, {this.props.name}</div>)}
}

加入 TypeScript

加入 TypeScript 后

interface Props {name?: string;
}class Greeting extends React.Component<Props, {}> {static defaultProps = {name: "stranger",};render() {return <div>Hello, {this.props.name}</div>;}
}

此时不支持直接通过类访问 defaultProps 来赋值以设置默认属性,因为 React.Component 类型上并没有该属性。

// ?Property 'defualtProps' does not exist on type 'typeof Greeting'.ts(2339)
Greeting.defualtProps = {name: "stranger",
};

默认属性的类型

上面虽然实现了通过 defaultProps 来指定属性的默认值,但 defaultProps 的类型是不受约束的,和 Props 没有关联上。以至于我们可以在 defaultProps 里面放任何值,显然这是不科学的。

class Greeting extends React.Component<Props, {}> {static defaultProps = {name: "stranger",// 并不会报错
+    foo: 1,
+    bar: {},};// ...
}

同时对于同一字段,我们不得不书写两次代码。一次是定义组件的 Props,另一次是在 defaultProps 里。如果属性有增删或名称有变更,两个地方都需要改。

为了后面演示方便,现在给组件新增一个必填属性 age:number

interface Props {age: number;name?: string;
}class Greeting extends React.Component<Props, {}> {static defaultProps = {name: "stranger",};render() {const { name, age } = this.props;return (<div>Hello, {name}, my age is {age}</div>);}
}

通过可选属性抽取出来,利用 typeof 获取其类型和必传属性结合来形成组件的 Props 可解决上面提到的两个问题。

所以优化后的代码成了:

const defaultProps = {name: "stranger",
};type Props = {age: number;
} & Partial<typeof defaultProps>;class Greeting extends React.Component<Props, {}> {static defaultProps = defaultProps;render() {const { name, age } = this.props;return (<div>Hello, {name}, my age is {age}</div>);}
}

注意我们的 Props 是通过和 typeof defaultProps 组合而形成的,可选属性中的 name 字段在整个代码中只书写了一次。

当我们更新了 defaultProps 时整个组件的 Props 也同步更新,所以 defaultProps 中的字段一定是组件所需要的字段。

默认值的判空检查优化

讲道理,如果属性提供了默认值,在使用时,可不再需要判空,因为其一定是有值的。但 TypeScript 在编译时并不知道,因为有默认值的属性是被定义成可选的 ?

比如我们尝试访问 name 属性的长度,

class Greeting extends React.Component<Props, {}> {static defaultProps = defaultProps;render() {const { name } = this.props;return (<div>{/* ?Object is possibly 'undefined'.ts(2532) */}name length is {name.length}</div>);}
}

因为此时我们的 Props 实际上是:

type Props = {age: number;
} & Partial<typeof defaultProps>;
// 相当于:
type Props = {age: number;name?: string;
};

修正方法有多个,最简单的是使用非空判定符/Non-null assertion operator。

非空判定符

- name length is {name.length}
+ name length is {name!.length}

这意味着每一处使用的地方都需要做类似的操作,当程序复杂起来时不太可控。但多数情况下应付日常使用,这样已经够了。

类型转换

因为组件内部有默认值的保证,所以字段不可能为空,因此,可对组件内部使用非空的属性类型来定义组件,而对外仍暴露原来的版本。

const Greeting = class extends React.Component<
-  Props,
+  Props & typeof defaultProps,{}
> {static defaultProps = defaultProps;render() {const { name } = this.props;return (<div>
-        name length is {name!.length}
+        name length is {name.length}</div>);}
-};
+} as React.ComponentClass<Props>;

通过 as React.ComponentClass<Props> 的类型转换,对外使用 Greeting 时属性中 name 还是可选的,但组件内部实际使用的是 Props & typeof defaultProps,而不是 Partial<T> 版本的,所以规避了字段可能为空的报错。

通过高阶组件的方式封装默认属性的处理

通过定义一个高阶组件比如 withDefaultProps 将需要默认属性的组件包裹,将默认值的处理放到高阶组件中,同样可解决上述问题。

function withDefaultProps<P extends object, DP extends Partial<P>>(dp: DP,component: React.ComponentType<P>,
) {component.defaultProps = dp;type RequiredProps = Omit<P, keyof DP>;return (component as React.ComponentType<any>) as React.ComponentType<RequiredProps & DP>;
}

然后我们的组件则可以这样来写:

const defaultProps = {name: "stranger",
};interface Props {name: string;age: number;
}const _Greeting = class extends React.Component<Props, {}> {public render() {const { name } = this.props;return <div>name length is {name.length}</div>;}
};export const Greeting = withDefaultProps(defaultProps, _Greeting);

这种方式就比较通用一些,将 withDefaultProps 抽取成一个公共组件,后续其他组件都可使用。但此种情况下就没有很好地利用已经定义好的默认值 defaultProps 中的字段,书写 Props 时还需要重复写一遍字段名。

相关资源

  • React docs - Default Prop Values
  • Default property value in React component using TypeScript
  • React, TypeScript and defaultProps dilemma

转载于:https://www.cnblogs.com/Wayou/p/react_typescript_default_props.html

React + TypeScript 默认 Props 的处理相关推荐

  1. react设置默认props

    一般设置props的默认值有两种方式,下面通过一个例子说明一下. <!DOCTYPE html> <html><head><meta charset=&quo ...

  2. TypeScript + React 学习render props

    ###前言 一直想学学TypeScript,尝试尝试带类型的js怎么写,有啥优点.正好这两天有时间结合着react写写小例子. 搭建环境 不想折腾webpack来自己配ts+react的环境就用typ ...

  3. Webpack 4 + React + Typescript 搭建启动模版

    2019年是个崭新的开始,在过去半年的工作中我参与到公司一个大型项目的维护和开发中,深深的体会到了react项目中数据流向复杂,参数类型错乱带来的痛苦体验,于是在崭新的一年我决定拥抱Typescrip ...

  4. React+TypeScript+webpack4多入口项目搭建

    资源 React-16.8.* react-router-dom-4.3.* TypeScript-3.5.* webpack-4.* eslint-5.16.* 项目目录 ├── dist # 打包 ...

  5. React TypeScript 从零实现 Popup 组件发布到 npm

    本文转载自掘金<从0到1发布一个Popup组件到npm>,作者「海秋」. 点击下方阅读原文去点个赞吧! 上篇文章[1]中介绍了如何从 0 到 1 搭建一个 React 组件库架子,但为了一 ...

  6. 【React+TS】从零开始搭建react+typescript+router+redux+less+px2rem自适应+sass+axios反向代理+别名@+Antd-mobile

    一.通过create-react-app脚手架创建项目 npx create-react-app testproject --template typescript 在vscode中打开项目,可以看到 ...

  7. 【React+TS】从零开始搭建react+typescript+router+redux+less+pxToVw自适应+sass+axios反向代理+别名@+Antd-mobile

    一.通过create-react-app脚手架创建项目 npx create-react-app testproject --template typescript  在vscode中打开项目,可以看 ...

  8. 字节 React + TypeScript 实践总结篇

    ❗️ 准备知识 熟悉 React 熟悉 TypeScript (参考书籍:2ality's guide[1], 初学者建议阅读:chibicode's tutorial[2]) 熟读 React 官方 ...

  9. React组件库实践:React + Typescript + Less + Rollup + Storybook

    背景 原先在做低代码平台的时候,刚好有搭载React组件库的需求,所以就搞了一套通用的React组件库模版.目前通过这套模板也搭建过好几个组件库. 为了让这个模板更干净和通用,我把所有和低代码相关的代 ...

最新文章

  1. 在java中必须要有main吗_在一个Java应用程序中main方法必须被说明为_____。
  2. C# Repeater绑定显示数组
  3. [Study Notes][001][gstreamer] memory
  4. python字符串一(字符串的书写输入输出)
  5. 网商微信实名认证FAQ
  6. 在运行hadoopdb\hive\hadoop源码时不能正确调用自定义core-site.xml等配置文件
  7. Html5 + fromData + Spring MVC 单文件、多文件上传
  8. beyond compare 代码对比工具使用方法
  9. 部署AdminLTE
  10. 三菱GX Work2 PLC工控仿真
  11. c语言中order函数,C语言order的用法
  12. Win10系统怎么安装cab文件?
  13. 零基础小白必须学习的,3Dmax快捷键大全!你都会吗?
  14. 咕咕王小谢的C语言之路————第一章 初遇C语言 第三集 变量与常量
  15. 树莓派4+普通机械硬盘+Netatalk搭建家用Nas+苹果TimeMachine
  16. 2.4 线性丢番图方程
  17. 蓝桥杯的比赛流程和必考点
  18. DEV05 GBase 8a MPP Cluster 数据库性能优化
  19. 宝塔Linux面板配置教程
  20. PS流包格式之PS/SYS/PSM/PES头

热门文章

  1. 分治法:BFPTR算法找第k小
  2. CodeForces Goodbye 2017
  3. 解析xml数据存入bean映射到数据库的 需求解决过程
  4. jenkins配置以cygwin环境的子节点
  5. [NOIP2006] 数列
  6. codeforces 269B Greenhouse Effect
  7. SSL:Ubuntu证书配置
  8. HDOJ 1428 漫步校园
  9. UIAlertView的使用方法
  10. iOS开发 小知识点