简谈创建React Component的几种方式


Posted in Javascript onJune 15, 2019

前言

当我们谈起React的时候,多半会将注意力集中在组件之上,思考如何将页面划分成一个个组件,以及如何编写可复用的组件。但对于接触React不久,还没有真正用它做一个完整项目的人来说,理解如何创建一个组件也并不那么简单。

在最开始的时候我以为创建组件只需要调用createClass这个api就可以了;但学习了ES6的语法后,又知道了可以利用继承,通过extends React.component来创建组件;后来在阅读别人代码的时候又发现了PureComponent以及完全没有继承,仅仅通过返回JSX语句的方式创建组件的方式。

下面这篇文章,就将逐一介绍这几种创建组件的方法,分析其特点,以及如何选择使用哪一种方式创建组件。

几种方法

1.createClass

如果你还没有使用ES6语法,那么定义组件,只能使用React.createClass这个helper来创建组件,下面是一段示例:

var React = require("react");
var Greeting = React.createClass({
propTypes: {
name: React.PropTypes.string //属性校验
},
getDefaultProps: function() {
return {
name: 'Mary' //默认属性值
};
},
getInitialState: function() {
return {count: this.props.initialCount}; //初始化state
},
handleClick: function() {
//用户点击事件的处理函数
},
render: function() {
return <h1>Hello, {this.props.name}</h1>;
}
});
module.exports = Greeting;

这段代码,包含了组件的几个关键组成部分,这种方式下,组件的props、state等都是以对象属性的方式组合在一起,其中默认属props和初始state都是返回对象的函数,propTypes则是个对象。这里还有一个值得注意的事情是,在createClass中,React对属性中的所有函数都进行了this绑定,也就是如上面的hanleClick其实相当于handleClick.bind(this) 。

2.component

因为ES6对类和继承有语法级别的支持,所以用ES6创建组件的方式更加优雅,下面是示例:

import React from 'react';
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
this.handleClick = this.handleClick.bind(this);
}
//static defaultProps = {
// name: 'Mary' //定义defaultprops的另一种方式
//}
//static propTypes = {
//name: React.PropTypes.string
//}
handleClick() {
//点击事件的处理函数
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
Greeting.propTypes = {
name: React.PropTypes.string
};
Greeting.defaultProps = {
name: 'Mary'
};
export default Greating;

可以看到Greeting继承自React.component,在构造函数中,通过super()来调用父类的构造函数,同时我们看到组件的state是通过在构造函数中对this.state进行赋值实现,而组件的props是在类Greeting上创建的属性,如果你对类的属性和对象的属性的区别有所了解的话,大概能理解为什么会这么做。

对于组件来说,组件的props是父组件通过调用子组件向子组件传递的,子组件内部不应该对props进行修改,它更像是所有子组件实例共享的状态,不会因为子组件内部操作而改变,因此将props定义为类Greeting的属性更为合理,而在面向对象的语法中类的属性通常被称作静态(static)属性,这也是为什么props还可以像上面注释掉的方式来定义。

对于Greeting类的一个实例对象的state,它是组件对象内部维持的状态,通过用户操作会修改这些状态,每个实例的state也可能不同,彼此间不互相影响,因此通过this.state来设置。

用这种方式创建组件时,React并没有对内部的函数,进行this绑定,所以如果你想让函数在回调中保持正确的this,就要手动对需要的函数进行this绑定,如上面的handleClick,在构造函数中对this 进行了绑定。

3.PureComponet

我们知道,当组件的props或者state发生变化的时候:React会对组件当前的Props和State分别与nextProps和nextState进行比较,当发现变化时,就会对当前组件以及子组件进行重新渲染,否则就不渲染。有时候为了避免组件进行不必要的重新渲染,我们通过定义shouldComponentUpdate来优化性能。例如如下代码:

class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}

shouldComponentUpdate通过判断props.color和state.count是否发生变化来决定需不需要重新渲染组件,当然有时候这种简单的判断,显得有些多余和样板化,于是React就提供了PureComponent来自动帮我们做这件事,这样就不需要手动来写shouldComponentUpdate了:

class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button
color={this.props.color}
onClick={() => this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
</button>
);
}
}

大多数情况下, 我们使用PureComponent能够简化我们的代码,并且提高性能,但是PureComponent的自动为我们添加的shouldComponentUpate函数,只是对props和state进行浅比较(shadow comparison),当props或者state本身是嵌套对象或数组等时,浅比较并不能得到预期的结果,这会导致实际的props和state发生了变化,但组件却没有更新的问题。

例如下面代码有一个ListOfWords组件来将单词数组拼接成逗号分隔的句子,它有一个父组件WordAdder让你点击按钮为单词数组添加单词,但他并不能正常工作:

class ListOfWords extends React.PureComponent {
render() {
return <div>{this.props.words.join(',')}</div>;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 这个地方导致了bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div>
<button onClick={this.handleClick} />
<ListOfWords words={this.state.words} />
</div>
);
}
}

这种情况下,PureComponent只会对this.props.words进行一次浅比较,虽然数组里面新增了元素,但是this.props.words与nextProps.words指向的仍是同一个数组,因此this.props.words !== nextProps.words 返回的便是flase,从而导致ListOfWords组件没有重新渲染,笔者之前就因为对此不太了解,而随意使用PureComponent,导致state发生变化,而视图就是不更新,调了好久找不到原因~。

最简单避免上述情况的方式,就是避免使用可变对象作为props和state,取而代之的是每次返回一个全新的对象,如下通过concat来返回新的数组:

handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}

你可以考虑使用Immutable.js来创建不可变对象,通过它来简化对象比较,提高性能。

这里还要提到的一点是虽然这里虽然使用了Pure这个词,但是PureComponent并不是纯的,因为对于纯的函数或组件应该是没有内部状态。

4.Stateless Functional Component
上面我们提到的创建组件的方式,都是用来创建包含状态和用户交互的复杂组件,当组件本身只是用来展示,所有数据都是通过props传入的时候,我们便可以使用Stateless Functional Component来快速创建组件。例如下面代码所示:

import React from 'react';
const Button = ({
day,
increment
}) => {
return (
<div>
<button onClick={increment}>Today is {day}</button>
</div>
)
}
Button.propTypes = {
day: PropTypes.string.isRequired,
increment: PropTypes.func.isRequired,
}

这种组件,没有自身的状态,相同的props输入,必然会获得完全相同的组件展示。因为不需要关心组件的一些生命周期函数和渲染的钩子,所以不用继承自Component显得更简洁。

对比

createClass vs Component

对于React.createClass 和 extends React.Component本质上都是用来创建组件,他们之间并没有绝对的好坏之分,只不过一个是ES5的语法,一个是ES6的语法支持,只不过createClass支持定义PureRenderMixin,这种写法官方已经不再推荐,而是建议使用PureComponent。

pureComponent vs Component

通过上面对PureComponent和Component的介绍,你应该已经了解了二者的区别:PureComponent已经定义好了shouldUpdateComponent而Component需要显示定义。

Component vs Stateless Functional component

Component包含内部state,而Stateless Functional Component所有数据都来自props,没有内部state;

1.Component 包含的一些生命周期函数,Stateless Functional Component都没有,因为Stateless Functional

2.component没有shouldComponentUpdate,所以也无法控制组件的渲染,也即是说只要是收到新的props,Stateless Functional Component就会重新渲染。

3.Stateless Functional Component 不支持Refs

选哪个?

这里仅列出一些参考:

1.createClass, 除非你确实对ES6的语法一窍不通,不然的话就不要再使用这种方式定义组件。

2.Stateless Functional Component, 对于不需要内部状态,且用不到生命周期函数的组件,我们可以使用这种方式定义组件,比如展示性的列表组件,可以将列表项定义为Stateless Functional Component。

3.PureComponent/Component,对于拥有内部state,使用生命周期的函数的组件,我们可以使用二者之一,但是大部分情况下,我更推荐使用PureComponent,因为它提供了更好的性能,同时强制你使用不可变的对象,保持良好的编程习惯。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 模仿vbs中的 DateAdd() 函数的代码
Aug 13 Javascript
JQuery 学习笔记 选择器之二
Jul 23 Javascript
JS关闭窗口与JS关闭页面的几种方法小结
Dec 17 Javascript
js防止DIV布局滚动时闪动的解决方法
Oct 30 Javascript
javascript生成随机数方法汇总
Nov 12 Javascript
使用node+vue.js实现SPA应用
Jan 28 Javascript
vue.js获取数据库数据实例代码
May 26 Javascript
jQuery实现模糊搜索功能的方法分析
Jun 29 jQuery
Vue基本使用之对象提供的属性功能
Apr 30 Javascript
vue轻量级框架无法获取到vue对象解决方法
May 12 Javascript
vue中node_modules中第三方模块的修改使用详解
May 31 Javascript
一百多行代码实现react拖拽hooks
Mar 23 Javascript
JS中的一些常用的函数式编程术语
Jun 15 #Javascript
JavaScript模块管理的简单实现方式详解
Jun 15 #Javascript
JavaScript工具库之Lodash详解
Jun 15 #Javascript
jQuery创建折叠式菜单
Jun 15 #jQuery
JavaScript的Proxy可以做哪些有意思的事儿
Jun 15 #Javascript
Async/Await替代Promise的6个理由
Jun 15 #Javascript
一些可能会用到的Node.js面试题
Jun 15 #Javascript
You might like
分页显示Oracle数据库记录的类之一
2006/10/09 PHP
php 字符串中的\n换行符无效、不能换行的解决方法
2014/04/02 PHP
php mb_substr()函数截取中文字符串应用示例
2014/07/29 PHP
php将HTML表格每行每列转为数组实现采集表格数据的方法
2015/04/03 PHP
学习php设计模式 php实现命令模式(command)
2015/12/08 PHP
Windows下PHP开发环境搭建教程(Apache+PHP+MySQL)
2016/06/13 PHP
PHP更安全的密码加密机制Bcrypt详解
2017/06/18 PHP
PHP进阶学习之类的自动加载机制原理分析
2019/06/18 PHP
phpStorm2020 注册码
2020/09/17 PHP
js对象关系图 方便dom操作
2012/03/18 Javascript
jQuery学习笔记之 Ajax操作篇(一) - 数据加载
2014/06/23 Javascript
jQuery中:selected选择器用法实例
2015/01/04 Javascript
JQuery中attr方法和removeAttr方法用法实例
2015/05/18 Javascript
jQuery插件实现表格隔行变色及鼠标滑过高亮显示效果代码
2016/02/25 Javascript
仿百度换肤功能的简单实例代码
2016/07/11 Javascript
javascript的几种写法总结
2016/09/30 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
2017/03/02 Javascript
Python UnicodeEncodeError: 'gbk' codec can't encode character 解决方法
2015/04/24 Python
对Python进行数据分析_关于Package的安装问题
2017/05/22 Python
动态规划之矩阵连乘问题Python实现方法
2017/11/27 Python
Python数据结构之哈夫曼树定义与使用方法示例
2018/04/22 Python
python 正确保留多位小数的实例
2018/07/16 Python
Python Cookie 读取和保存方法
2018/12/28 Python
python根据多个文件名批量查找文件
2019/08/13 Python
Python中sorted()排序与字母大小写的问题
2020/01/14 Python
Python Tornado之跨域请求与Options请求方式
2020/03/28 Python
python实现坦克大战
2020/04/24 Python
Python读入mnist二进制图像文件并显示实例
2020/04/24 Python
基于python计算并显示日间、星期客流高峰
2020/05/07 Python
selenium+headless chrome爬虫的实现示例
2021/01/08 Python
迪奥官网:Dior.com
2018/12/04 全球购物
简述数据库的设计过程
2015/06/22 面试题
支行行长竞聘演讲稿
2014/05/15 职场文书
优秀党员先进事迹材料
2014/12/18 职场文书
公司表扬信格式
2015/05/04 职场文书
孟佩杰观后感
2015/06/17 职场文书