一文详解 React 组件类型
本文的目标是让开发者清晰地了解 React 组件类型,哪些在现代 React 应用中依然在使用,以及为何一些类型现在不再使用了。
作者 | Robin Wieruch
译者 | 弯月
责编 | 屠敏
出品 | CSDN(ID:CSDNNews)
以下为译文:
尽管React从2013年发布到现在并没有引入太多重大改变,但不同类型的React组件也出现了不少。一些组件类型和组件设计模式今天依然在使用,它们已成了构建React应用程序的标准,而另一些类型的组件只会在旧的应用和新手教学中出现。
在这篇文章中,我想通过层次化的方式向初学React的人介绍一下不同的React组件和React设计模式。读完本文后,你应当能够从旧的应用和入门文章中分辨出不同类型的React组件,并能够自信地编写自己的现代React组件。
React createClass组件
我们要从React的createClass组件说起。createClass方法为开发者提供了一个工厂方法,无需编写JavaScript类就可以创建React类组件。在JavaScript ES6出现之前,这种方法是创建React组件的标准方法,因为在JavaScript ES5时代还无法使用类语法:
var App = React.createClass({ getInitialState: function() { return { value: '', }; },
onChange: function(event) { this.setState({ value: event.target.value }); },
render: function() { return ( <div> <h1>Hello React "createClass" Component!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); },});
createClass()工厂方法接受一个对象,该对象定义了React组件中的方法。其中,getInitialState()方法用来设置React组件的初始状态,还有必须的render()方法用来显示JSX形式的组件。给对象传递更多函数即可添加更多的“方法”(如onChange())。
还可以使用生命周期方法来管理副作用。例如,为了随时将输入框中的值保存到浏览器的local storage中,可以利用componentDidUpdate()这个生命周期方法,只需要将该函数传递给工厂函数的对象即可。而且,local storage中的值也可以在组件接收初始状态的时候读出来:
var App = React.createClass({ getInitialState: function() { return { value: localStorage.getItem('myValueInLocalStorage') || '', }; },
componentDidUpdate: function() { localStorage.setItem('myValueInLocalStorage', this.state.value); },
onChange: function(event) { this.setState({ value: event.target.value }); },
render: function() { return ( <div> <h1>Hello React "createClass" Component!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); },});
每次重新加载或刷新浏览器时,之前在输入框中输入过的、保存在local storage中的初始状态就会在组件初次mount的时候显示出来。
注意:React的createClass方法现在已经不在React的核心包中了。如果你想尝试下,就必须要安装另一个包:npm install create-react-class。
React Mixin
React Mixin是在React提出可重用组件逻辑的高级设计方式时加入的。利用Mixin可以将React组件中的逻辑提取出来作为独立的对象使用。在使用Mixin对象时,Mixin中的所有功能都会被引入到组件中:
var localStorageMixin = { getInitialState: function() { return { value: localStorage.getItem('myValueInLocalStorage') || '', }; },
setLocalStorage: function(value) { localStorage.setItem('myValueInLocalStorage', value); },};
var App = React.createClass({ mixins: [localStorageMixin],
componentDidUpdate: function() { this.setLocalStorage(this.state.value); },
onChange: function(event) { this.setState({ value: event.target.value }); },
render: function() { return ( <div> <h1>Hello React "createClass" Component with Mixin!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); },});
在这个例子中,Mixin提供了组件的初始状态,而该初始状态是从local storage中读取的,并且还利用setLocalStorage()扩展原来的组件,该函数之后会在组件中被调用。为了让Mixin更灵活,我们可以使用一个返回函数的对象:
function getLocalStorageMixin(localStorageKey) { return { getInitialState: function() { return { value: localStorage.getItem(localStorageKey) || '' }; },
setLocalStorage: function(value) { localStorage.setItem(localStorageKey, value); }, };}
var App = React.createClass({ mixins: [getLocalStorageMixin('myValueInLocalStorage')],
...});
不过,现代React应用程序已经不再使用Mixin了,因为它们会带来一些负面作用。关于Mixin的细节和消亡过程可以阅读这里(https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)
React类组件
React类组件是在JavaScript ES6时引入的,因为直到ES6才支持JS类。有时候它们也被称为React ES6类组件。至少有了JavaScript ES6之后,就不需要使用React的createClass方法了。JS自己终于支持类了:
class App extends React.Component { constructor(props) { super(props);
this.state = { value: '', };
this.onChange = this.onChange.bind(this); }
onChange(event) { this.setState({ value: event.target.value }); }
render() { return ( <div> <h1>Hello React ES6 Class Component!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); }}
使用JavaScript类编写的React Component有个类似于类构造器的方法,主要用于让React设置初始状态,或者绑定方法。还有必须的render方法用于返回JSX的输出。React组件的所有内部逻辑都通过类组件定义中的面向对象继承,从extends React.Component获得。但是,除了这种用法之外,我并不推荐进一步使用类继承,相反,应当主要使用类组合(composition)。
注意:利用JavaScript类定义React组件时还可以使用了另一种语法,通过JavaScript ES6的箭头函数来自动绑定React组件中的方法:
class App extends React.Component { constructor(props) { super(props);
this.state = { value: '', }; }
onChange = event => { this.setState({ value: event.target.value }); };
render() { return ( <div> <h1>Hello React ES6 Class Component!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); }}
React类组件提供几个生命周期方法,用于mount、update和unmount等。比如前面的local storage的例子,可以在生命周期方法中以副作用的方式来执行这些操作——即,将输入框中的最新值保存到local storage中,而在构造函数中可以根据local storage的值来设置初始状态:
class App extends React.Component { constructor(props) { super(props);
this.state = { value: localStorage.getItem('myValueInLocalStorage') || '', }; }
componentDidUpdate() { localStorage.setItem('myValueInLocalStorage', this.state.value); }
onChange = event => { this.setState({ value: event.target.value }); };
render() { return ( <div> <h1>Hello React ES6 Class Component!</h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); }}
利用this.state、this.setState()和生命周期方法,React类组件中的状态管理和副作用可以写在一起。React类组件到现在依然在广泛使用,尽管后文即将介绍的React函数组件在现代React应用程序中得到了更广泛的应用,因为函数组件已经不逊于类组件了。
React高阶组件
React高阶组件(Higher-Order Components,简称 HOC)是一种React的高级设计模式,是替代Mixin的另一种在组件间复用逻辑的方法。如果你没听说过HOC,可以读一读我的另一篇入门文章:高阶组件(https://www.robinwieruch.de/gentle-introduction-higher-order-components/)。简单来说,高阶组件就是接收一个组件作为输入,然后输出另一个组件(并扩展其功能)的组件。我们利用前面的例子来看看,怎样才能将功能提取到可复用的高阶组件中。
const withLocalStorage = localStorageKey => Component => class WithLocalStorage extends React.Component { constructor(props) { super(props);
this.state = { [localStorageKey]: localStorage.getItem(localStorageKey), }; }
setLocalStorage = value => { localStorage.setItem(localStorageKey, value); };
render() { return ( <Component {...this.state} {...this.props} setLocalStorage={this.setLocalStorage} /> ); } };
class App extends React.Component { constructor(props) { super(props);
this.state = { value: this.props['myValueInLocalStorage'] || '' }; }
componentDidUpdate() { this.props.setLocalStorage(this.state.value); }
onChange = event => { this.setState({ value: event.target.value }); };
render() { return ( <div> <h1> Hello React ES6 Class Component with Higher-Order Component! </h1>
<input value={this.state.value} type="text" onChange={this.onChange} />
<p>{this.state.value}</p> </div> ); }}
const AppWithLocalStorage = withLocalStorage('myValueInLocalStorage')(App);
另一个高级React设计模式就是React Render Prop组件,通常代替React高阶组件使用。我不在给出这种抽象的例子,更多内容请查看网上的一些教程。
React高阶组件和React Render Prop组件在今天都在被广泛使用,尽管React函数组件和React钩子(后文会介绍)也许对于React组件的抽象更好。不过,高阶组件和Render Prop也可以用在函数组件上。
React函数组件
React函数组件等价于React类组件,但它表现为一个函数,而不是一个类。过去函数组件没有状态也无法使用副作用,因此它们被称为“无状态函数组件”,但自从React钩子出现后,函数组件就复活了。
React钩子给函数组件带来了状态和副作用。React不仅带有各种内置的钩子,还允许创建自定义的钩子。我们来看看前面的类组件的例子怎样改写成函数组件:
const App = () => { const [value, setValue] = React.useState('');
const onChange = event => setValue(event.target.value);
return ( <div> <h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p> </div> );};
这段代码仅在输入框上演示了函数组件。由于要捕获输入框的值,就需要使用组件状态,因此这里用到了内置的React.useState钩子。
React钩子还可以在函数组件中实现副作用。一般来说,内置的useEffect钩子可以用来在任何props或state发生变化时执行一个函数:
const App = () => { const [value, setValue] = React.useState( localStorage.getItem('myValueInLocalStorage') || '', );
React.useEffect(() => { localStorage.setItem('myValueInLocalStorage', value); }, [value]);
const onChange = event => setValue(event.target.value);
return ( <div> <h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p> </div> );};
这段代码演示了useEffect钩子的用法,每次状态中的输入框的值改变时,该钩子就会被执行。当提供给useEffect钩子的函数被执行时,它会利用最新的值更新local storage中的值。此外,函数组件的初始状态也可以使用useState钩子从local storage中读取。
最后一点,我们可以将讲个钩子提取出来,封装成一个自定义钩子,这样可以保证组件状态永远和local storage同步。它在最后会返回一个值和setter函数,供函数组件使用:
const useStateWithLocalStorage = localStorageKey => { const [value, setValue] = React.useState( localStorage.getItem(localStorageKey) || '', );
React.useEffect(() => { localStorage.setItem(localStorageKey, value); }, [value]);
return [value, setValue];};
const App = () => { const [value, setValue] = useStateWithLocalStorage( 'myValueInLocalStorage', );
const onChange = event => setValue(event.target.value);
return ( <div> <h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p> </div> );};
由于这段代码是从函数组件中提取出来的,它可以用于任何其他组件,以实现业务逻辑的复用。它与Mixin、高阶组件和Render Prop组件一样都是高级设计模式。但是需要指出的是,React的函数组件也可以用高阶组件和Render Prop组件来增强。
React函数组件、钩子和类组件是目前编写现代React应用程序的标准。但是,我坚信以后函数组件和钩子将取代类组件。届时,类组件也许只会出现在旧的应用程序和教程中,就像今天的 createClass组件和Mixin一样。高阶组件和Render Prop组件也同理,它们也会被钩子取代。
写在最后
所有React的组件在Pros的用法方面的理念都是一样的,都是将信息沿着组件树向下传递。但是,类组件和函数组件对于状态和副作用的用法是不同的,还有生命周期方法和钩子。
这篇文章介绍了所有不同种类的React组件及其用法,以及它们在历史中的位置。最后总结一下,现在使用类组件、函数组件和钩子、高阶组件和Render Prop组件等高级概念是完全没问题的。但是也应当了解到,旧的React应用程序和教程也会使用一些只有以前才使用的旧组件和设计模式。
原文:https://www.robinwieruch.de/react-component-types/
本文为 CSDN 翻译,如需转载,请注明来源出处。作者独立观点,不代表 CSDN 立场。
【完】
热 文 推 荐
豆瓣 9.0,评论人数过万的 9 本经典科技图书 | 码书排行榜
☞开源告急?!
任正非:美国迟早会爱上华为
☞18 岁少年盗取价值 90 万元的萌乃币, 交易所被迫关停!
李笑来登顶 GitHub TOP 榜!币圈大佬要教程序员如何自学编程
☞马云:蚂蚁金服这样做区块链!
女生适合做程序员吗?
Google首页玩起小游戏,AI作曲让你变身巴赫
曝光!月薪 5 万的程序员面试题:73% 人都做错,你敢试吗?
System.out.println("点个在看吧!");
console.log("点个在看吧!");
print("点个在看吧!");
printf("点个在看吧!\n");
cout << "点个在看吧!" << endl;
Console.WriteLine("点个在看吧!");
Response.Write("点个在看吧!");
alert("点个在看吧!")
echo "点个在好看吧!"
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
喜欢就点击“好看”吧!
一文详解 React 组件类型相关推荐
- 一文详解自动驾驶的动态驾驶任务(DDT) | 自动驾驶系列
文章版权所有,未经授权请勿转载或使用 本系列上篇文章<一文详解自动驾驶的运行设计域(ODD)>解读了什么是自动驾驶ODD,本篇文章依据SAE J3016详细解读自动驾驶DDT.DDT fa ...
- storybook组件属性详解:组件props到strorybook Args
首先我们查看官方文档:ArgsTable 官方的例子么有看到v-model如何处理,数组.对象等复杂属性定义. 这里一个是props的定义,一个是Controls 先看一下官方文档,Controls ...
- 一文详解决策树算法模型
AI有道 一个有情怀的公众号 上文我们主要介绍了Adaptive Boosting.AdaBoost演算法通过调整每笔资料的权重,得到不同的hypotheses,然后将不同的hypothesis乘以不 ...
- 「软件项目管理」一文详解软件配置管理计划
一文详解软件配置管理计划 前言 一.配置管理概述 1. 配置管理(SCM)定义 2. 软件配置项目(SCI) 3. 基线 4. 软件配置控制委员会(SCCB) 二.软件配置管理过程 1. 管理过程 2 ...
- 详解JavaScript变量类型判断及domReady原理 写得很好
原文:详解JavaScript变量类型判断及domReady原理 我们知道,在开发JavaScript时候,经常要判断JavaScript变量类型,此 JavaScript教程 详细介绍JS变量的判断 ...
- mybatis 鉴别其_MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询
MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询 白玉 IT哈哈 支持的 JDBC 类型 为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 ...
- Python-Matplotlib可视化(1)——一文详解常见统计图的绘制
Python-Matplotlib可视化(1)--一文详解常见统计图的绘制 matplotlib库 曲线图 曲线图的绘制 结合Numpy库,绘制曲线图 绘制多曲线图 读取数据文件绘制曲线图 散点图 条 ...
- 一文详解Pandas
一文详解Pandas 一.Pandas概述 二.Pandas数据结构 2.1 Series 2.2 DataFrame数据结构 二.数学与统计计算 三.DataFrame的文件操作 3.1 读取文件 ...
- 从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘
本文转载自:从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘 事务消息是RocketMQ提供的非常重要的一个特性,在4.x版本之后开源,可以利用事务消息轻松地实现分布式事务.本文对Roc ...
最新文章
- NFS基本配置与NFS客户端自动挂载
- 最大独立集 HDU 1068
- 小程序入门学习08--云开发01
- 总结几个与模块相关的命令
- float和position
- 局域网文件共享服务器软件,局域网文件夹加密工具哪个好?文件共享服务器软件介绍...
- 深入浅出数据分析pdf
- java计算机毕业设计校园流浪猫图鉴管理系统的设计与实现源码+数据库+系统+lw文档+部署
- 微信小程序毕业设计 就餐预约点餐小程序毕业设计
- Win7英文版打补丁遇到的问题汇总
- 微x怎么设置主题_团日活动主题策划书范文
- REUSE_ALV_GRID_DISPLAY_LVC函数输入参数属性的应用
- 视频合并软件怎么把多个视频合并为一个视频
- Attention原理详解
- C语言中的复制函数(strcpy和memcpy)
- 数据库原理与设计P163习题9答案
- 烟台大学—贺利坚的计算机课程教学链接
- 计算机 社会网络分析,社会网络分析的工具.ppt
- classes是什么意思怎么读_淦怎么读音 淦是什么意思
- 月历(从星期一开始)
热门文章
- linux 查看端口战役,漫画 :Apache Nginx80 端口争夺战
- 公钥 私钥_公钥 私钥 签名 验签 说的啥?
- java 银行卡号格式化_JS银行卡号格式化 - JavaScript常用效果 - Front-End - NalanXue's Blog...
- Python浅拷贝与深拷贝
- Ubuntu16.06LTS安装gnome-3.8桌面
- 2021-2025年中国冻融室行业市场供需与战略研究报告
- 多路径配置udev_多路径multipath配置,udev绑定
- arduino 按钮读取_Arduino内置教程-数字-检测按键状态
- 用element-ui的走马灯carousel轻松实现自适应全屏banner图 解决el-carousel默认高度300问题 组件代码
- Clipsync – 同步 Win 和 Android 剪贴板