静态类型检查
像 Flow 和 TypeScript 等这些静态类型检查器,可以在运行前识别某些类型的问题。他们还可以通过增加自动补全等功能来改善开发者的工作流程。出于这个原因,我们建议在大型代码库中使用Flow
或TypeScript
来代替PropTypes
。(引自官网)
在这一小节中,主要讲的就是如何使用 Flow
和 TypeScript
来实现对静态类型的检测,如果项目是通过 create-react-app
构建的,那么这两个方案都是已经能够开箱即用的,只需要新增比较简单的配置即可。如 Flow
:
// 1、在项目中新增Flow
yarn add --dev flow-bin
// 2、在package.json中的scripts里加入
"scripts":{
...,
"flow": "flow"
}
// 1、运行flow
yarn flow
以上就是简单的 Flow
的配置, 只要在项目中使用了 //@flow
注释的页面, Flow
就会对该页面进行检测,最后返回是否有异常。随后说的是 TypeScript
的配置:
TypeScript 是一种由微软开发的编程语言。它是 JavaScript 的一个类型超集,包含独立的编译器。作为一种类型语言,TypeScript 可以在构建时发现 bug 和错误,这样程序运行时就可以避免此类错误。(引自官网)
都知道,在使用TS进行编程的时候是必须要进行类型的定义的,这自然就能在使用的时候就进行验证。create-react-app
已经配置好了TS的运行环境,可以通过 npx create-react-app my-app --template typescript
进行创建项目,如果是在现有的项目中新增,需要先安装: yarn add typescript @types/node @types/react @types/react-dom @types/jest
。随后将所有js类文件重命名,如(src/index.js
替换成 src/index.tsx
)
PS: 对于自己通过 webpack
进行配置的用户来说, Flow
需要在编译的时候去除其语法,不然会报错:具体配置见 flow-remove-types
严格模式
严格模式有点类似于前文的 Profiler
的用法,不过严格模式是通过 StrictMode
检测内部组件的合法性,目前主要用于一下几项:
(引自官网)
所谓不安全的生命周期主要是针对用于更新的几个生命周期在调用时出现的异常,在异步渲染之更新这里面进行了描述,在下面的引用中,react
特意对 componentWillMount
、componentWillReceiveProps
、componentWillUpdate
设置了以 UNSAFE_
开头的别名,可见这些更新在 react
看来是不安全的。此外,在 React17.0
之后将新增生命周期来完善现有的更新机制: getDerivedStateFromProps
和 getSnapshotBeforeUpdate
。
从概念上讲,React 分两个阶段工作:
- 渲染 阶段会确定需要进行哪些更改,比如 DOM。在此阶段,React 调用
render
,然后将结果与上次渲染的结果进行比较。- 提交 阶段发生在当 React 应用变化时。(对于 React DOM 来说,会发生在 React 插入,更新及删除 DOM 节点的时候。)在此阶段,React 还会调用
componentDidMount
和componentDidUpdate
之类的生命周期方法。提交阶段通常会很快,但渲染过程可能很慢。因此,即将推出的 concurrent 模式 (默认情况下未启用) 将渲染工作分解为多个部分,对任务进行暂停和恢复操作以避免阻塞浏览器。这意味着 React 可以在提交之前多次调用渲染阶段生命周期的方法,或者在不提交的情况下调用它们(由于出现错误或更高优先级的任务使其中断)。
渲染阶段的生命周期包括以下 class 组件方法:
constructor
componentWillMount
(orUNSAFE_componentWillMount
)componentWillReceiveProps
(orUNSAFE_componentWillReceiveProps
)componentWillUpdate
(orUNSAFE_componentWillUpdate
)getDerivedStateFromProps
shouldComponentUpdate
render
setState
更新函数(第一个参数)因为上述方法可能会被多次调用,所以不要在它们内部编写副作用相关的代码,这点非常重要。忽略此规则可能会导致各种问题的产生,包括内存泄漏和或出现无效的应用程序状态。不幸的是,这些问题很难被发现,因为它们通常具有非确定性。
严格模式不能自动检测到你的副作用,但它可以帮助你发现它们,使它们更具确定性。通过故意重复调用以下函数来实现的该操作:
- class 组件的
constructor
,render
以及shouldComponentUpdate
方法- class 组件的生命周期方法
getDerivedStateFromProps
- 函数组件体
- 状态更新函数 (即
setState
的第一个参数)- 函数组件通过使用
useState
,useMemo
或者useReducer
(以上引自官网)
使用 PropTypes 类型检查
这一个模块就是说了 PropTypes
怎么在 react
中使用,以及大部分的判定函数是如何书写的,由于类型太多我就不复制黏贴了,后面如果想要知道就去官网自己看就行,上面的标题是直达链接。
非受控组件
对于一个接受参数并返回相应结果的组件来说,分为非受控组件和受控组件,其参数也分为受控参数和非受控参数。以 antd4.0
为例来看, defaultValue
这一参数基本上都是非受控的,而 value
则是受控的,你在外层可以控制内部的函数渲染,外层的更改会传递到内部进行修改。
通常来说,使用 setState
还是使用 ref
来获得数据,主要还是根据当前的需求来决定的,如果你不需要动态的更新,则使用 ref
就很方便,反之亦然,下面写了一个感觉是假的非受控组件的 form
表单,模仿 antd
的 Form
,用到了高级指引中学到的几种方法以及 React.Children
:
/**
* @desc 用于测试NotContral的页面
* @name NotContral
* @author Cyearn
* @date 2020/11/21
*/
import React, { Component } from "react";
import styles from "./styles.module.less";
class NotContral extends Component {
constructor(props) {
super(props);
this.state = {};
this.ref = React.createRef();
}
componentDidMount() {}
submit = () => {
const { setValue, getValue } = this.ref;
console.log(getValue());
};
render() {
return (
);
}
}
const Form = function (props) {
const { children, refForm } = props;
const ReForm = withForm(children, refForm);
return (
test
);
};
function withForm(children, refForm) {
if (children) {
return class extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
let param = {};
// 向子组件注入默认值,onChange事件
this.Children = React.Children.map(children, (child) => {
param[child.props.name] = "";
let key = child.props.name;
return React.cloneElement(child, {
onChange: this.changeInput,
defaultValue: this.state[key],
value: this.state[key],
}); //这次我们通过React.cloneElement添加属性
});
this.setState({
...param,
});
}
// 定义设置值得函数
setValue = (obj = {}) => {
this.setState({
...obj,
});
};
// 定义获取值得函数
getValue = () => {
const all = this.state;
return { ...all };
};
// 当表单内数据发生变化的函数
changeInput = (e, id) => {
let param = {};
param[id] = e;
this.setState({
...param,
});
};
onValueChange = () => {};
render() {
return ;
}
};
}
}
// HOC
function WithItem(Inject, props) {
class Test extends Component {
constructor(props) {
super(props);
this.state = { value: "" };
}
render() {
const { value } = props;
console.log(value);
return (
props.onChange(e, props.name)}
value={value}
/>
);
}
}
return Test;
}
// 包裹子节点
class Item extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { label, children, ...other } = this.props;
let type = children && children.type;
if (type) {
const Children = WithItem(this.props.children.type, { ...other });
return (
{label}:
);
} else {
return null;
}
}
}
// 最小节点
function Input(props) {
const { onChange, value } = props;
return (
onChange(e.target.value)}
value={value}
/>
);
}
export default NotContral;