组件可以将 UI 切分成一些独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。
组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的 React 元素
react 组件的名称,首字母一律大写,并且使用驼峰写法

类定义组件

  • state
  • props
  • lifecircle

如果没有组件私有状态(state)需要去定义,根据 es6 class 的原则,可以省略不写 constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import React from "react";
class App extends React.Component {
// state 写法一:
constructor(props) {
super(props);
this.state = {};
}
// state 写法二:
state = {};
//通过render方法渲染组件的模板
render() {
// 只能返回一个根结点元素,否则报错
return <div></div>;
}
}

// 低版本react 解构了 Component,依然需要引入 React, 否则报错
import React, { Component } from "react";
// react v16.8以上不在需要引入 React
import { Component } from "react";

class App extends Component {
//通过render方法渲染组件的模板,并且render只能返回一个根节点元素
render() {
// 只能返回一个根结点元素,否则报错
return <div></div>;
}
}
import React from "react";
import ReactDOM from "react-dom";

const template = <div></div>;
const element = document.getElementById("#app");
ReactDOM.render(template, element);

组件&纯组件

用户进行操作时数据时,通过生命周期钩子函数 **shouldComponentUpdate **判断新值与旧值是否一致来确定是否更新模版。
这样可以减少页面重绘和回流,提升用户体验、网站性能。

由于大量的判断也很繁琐,我们可以通过PureComponent定义组件,完成上述效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
import React, { Component, PureComponent } from "react";
// 类组件
class App extends Component {
render() {
return <div></div>;
}
}
// 纯组件
class App extends PureComponent {
render() {
return <div></div>;
}
}

函数式组件

函数式组件又被称为无状态组件(下文一律称-无状态组件)
该函数是一个有效的 React 组件,它接收一个单一的“props”对象并返回了一个 React 元素
我们之所以称这种类型的组件为函数定义组件,是因为从字面上来看,它就是一个 JavaScript 函数。
定义组件时,render 返回的 jsx 模板如果有多个子节点,那么给组件的根节点元素最好用小括号包起来

特点

  • 没有生命周期钩子函数
  • 没有 state
  • 没有 this
  • 必须返回一段 jsx 代码
1
2
3
4
5
6
7
8
9
const ProductList = (props) => {
// 只能返回一个根结点元素,否则报错
return (
<div>
this is productList
<p></p>
</div>
);
};

es5 创建组件

1
2
3
4
5
var ProductList = React.createClass({
render: function () {
return <div>this is productList</div>;
},
});

注意事项

  • 无状态组件 prop 不要通过 this.props 来调用
  • 无状态组件没有生命周期
  • 无状态组件不能定义定义状态(state)
  • 无状态组件不能实例化,组件名不能被 new
  • 无状态组件只能返回一个根节点元素
  • 类组件 render 函数,只能返回一个根节点元素
  • 类组件如果显式的声明了 constructor, 必须调用 super

组件属性

  • 内部属性-state

this.state.name=”123” ❌ 不可以直接修改如果修改了不会发生页面重绘
需要通过 this.setState() 对号

props

  • 动态 props
  • 静态 props

PropTypes 类型检查

为什么需要类型检查?

JavaScript 是一门弱类型的语言,允许变量类型做隐式转换。也正是因为这个特性,JavaScript 中有很多错误都是类型错误导致的。为了减少这种错误,我们可以在 React 中引入类型检查模块。

React 中的类型检查:prop-types 包

例子

  • 导入包
1
import PropTypes from "prop-types";
  • 编写组件
1
2
3
4
5
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
  • 新增类型检查
1
2
3
Greeting.propTypes = {
name: PropTypes.string,
};
  • 检验类型检查
    • 把 Greeting 组件的属性 name 设置成 String 类型,也就是说父组件通过该属性传递数据的时候,需要传递 String 类型的数据,否则会显示类型错误的警告。

我们可以尝试传递一个数字类型的属性:

1
2
3
<div>
<Greeting name={123} />
</div>

可以看到页面中会出现类型错误的警告:

PropTypes 包含一整套验证器,可用于确保接收的数据是有效的。在上面的示例中,我们使用了 PropTypes.string。
出于性能原因,propTypes 只在开发模式下进行检查。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import PropTypes from "prop-types";

MyComponent.propTypes = {
// 属性可以声明为 JS 原生类型
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,

/* 任何东西都可以被渲染:numbers, strings, elements,或者是包含这些类型的数组(或者是片段)。*/
optionalNode: PropTypes.node,

// 一个 React 元素。
optionalElement: PropTypes.element,

// 属性也可以声明为类的一个实例。
// 使用 JS 的 instanceof 运算符。
optionalMessage: PropTypes.instanceOf(Message),

// 属性声明为特定的值,类似于枚举
optionalEnum: PropTypes.oneOf(["News", "Photos"]),

// 一个对象可以是多种类型其中之一
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message),
]),

// 一个某种类型的数组
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

// 属性值为某种类型的对象
optionalObjectOf: PropTypes.objectOf(PropTypes.number),

// 一个特定形式的对象
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number,
}),

// 使用 'isRequired' 链接上述的类型设定,可以确保在没有提供 Props 的情况下显示警告。
requiredFunc: PropTypes.func.isRequired,

// 任何数据类型的值
requiredAny: PropTypes.any.isRequired,

/* 属性也可以声明为自定义的验证器。验证失败则返回 Error 对象。不要使用 `console.warn` 或者 throw ,*/
// 因为这不会在 `oneOfType` 类型的验证器中起作用。
customProp: function (props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
"Invalid prop `" +
propName +
"` supplied to" +
" `" +
componentName +
"`. Validation failed."
);
}
},

// 属性可以声明`arrayOf`和`objectOf`类型的验证器,如果验证失败,则需要返回Error对象。
// 可以在数组或者对象的每一个元素上调用验证器。验证器的前两个参数分别是数组或者对象本身和当前元素的键值。
customArrayProp: PropTypes.arrayOf(function (
propValue,
key,
componentName,
location,
propFullName
) {
if (!/matchme/.test(propValue[key])) {
return new Error(
"Invalid prop `" +
propFullName +
"` supplied to" +
" `" +
componentName +
"`. Validation failed."
);
}
}),
};

使用 PropTypes.element 限制单个元素

  • 可以通过 PropTypes.element 来确保传递给组件的 children 中只包含一个元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
import PropTypes from "prop-types";

class MyComponent extends React.Component {
render() {
// 这必须只有一个元素,否则控制台会打印警告。
const children = this.props.children;
return <div>{children}</div>;
}
}

MyComponent.propTypes = {
children: PropTypes.element.isRequired,
};

this.props.children 是父组件在 MyComponent 中添加的子节点,当 children 包含多个兄弟节点,而不是只有一个节点时,会打印错误警告。

属性默认值

  • 可以通过配置 defaultProps 为 Props 定义默认值:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Greeting extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

// 指定 props 的默认值:
Greeting.defaultProps = {
name: "Stranger",
};

// 渲染出 "Hello, Stranger":
ReactDOM.render(<Greeting />, document.getElementById("example"));

如果正在使用像 transform-class-properties 的 Babel 转换工具,也可以在 React 组件类中声明 defaultProps 作为静态属性。

1
2
3
4
5
6
7
8
9
class Greeting extends React.Component {
static defaultProps = {
name: "stranger",
};

render() {
return <div>Hello, {this.props.name}</div>;
}
}

defaultProps 用来确保 this.props.name 在父组件没有特别指定的情况下,有一个初始值。类型检查发生在 defaultProps 赋值之后,所以类型检查也会应用在 defaultProps 上面,我们也需要保证所设置的默认值符合类型检查设定的类型。

函数组件

如果在常规开发中使用函数组件,那可能需要做一些适当的改动,以保证 PropsTypes 应用正常。

假设你有如下组件:

1
2
3
export default function HelloWorldComponent({ name }) {
return <div>Hello, {name}</div>;
}

如果要添加 PropTypes,你可能需要在导出之前以单独声明的一个函数的形式,声明该组件,具体代码如下:

1
2
3
4
5
function HelloWorldComponent({ name }) {
return <div>Hello, {name}</div>;
}

export default HelloWorldComponent;

接着,可以直接在 HelloWorldComponent 上添加 PropTypes:

1
2
3
4
5
6
7
8
9
10
11
import PropTypes from "prop-types";

function HelloWorldComponent({ name }) {
return <div>Hello, {name}</div>;
}

HelloWorldComponent.propTypes = {
name: PropTypes.string,
};

export default HelloWorldComponent;