本文的目标是让开发者清晰地了解 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 组件类型相关推荐

  1. 一文详解自动驾驶的动态驾驶任务(DDT) | 自动驾驶系列

    文章版权所有,未经授权请勿转载或使用 本系列上篇文章<一文详解自动驾驶的运行设计域(ODD)>解读了什么是自动驾驶ODD,本篇文章依据SAE J3016详细解读自动驾驶DDT.DDT fa ...

  2. storybook组件属性详解:组件props到strorybook Args

    首先我们查看官方文档:ArgsTable 官方的例子么有看到v-model如何处理,数组.对象等复杂属性定义. 这里一个是props的定义,一个是Controls 先看一下官方文档,Controls ...

  3. 一文详解决策树算法模型

    AI有道 一个有情怀的公众号 上文我们主要介绍了Adaptive Boosting.AdaBoost演算法通过调整每笔资料的权重,得到不同的hypotheses,然后将不同的hypothesis乘以不 ...

  4. 「软件项目管理」一文详解软件配置管理计划

    一文详解软件配置管理计划 前言 一.配置管理概述 1. 配置管理(SCM)定义 2. 软件配置项目(SCI) 3. 基线 4. 软件配置控制委员会(SCCB) 二.软件配置管理过程 1. 管理过程 2 ...

  5. 详解JavaScript变量类型判断及domReady原理 写得很好

    原文:详解JavaScript变量类型判断及domReady原理 我们知道,在开发JavaScript时候,经常要判断JavaScript变量类型,此 JavaScript教程 详细介绍JS变量的判断 ...

  6. mybatis 鉴别其_MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询

    MyBatis之Mapper XML 文件详解(四)-JDBC 类型和嵌套查询 白玉 IT哈哈 支持的 JDBC 类型 为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 ...

  7. Python-Matplotlib可视化(1)——一文详解常见统计图的绘制

    Python-Matplotlib可视化(1)--一文详解常见统计图的绘制 matplotlib库 曲线图 曲线图的绘制 结合Numpy库,绘制曲线图 绘制多曲线图 读取数据文件绘制曲线图 散点图 条 ...

  8. 一文详解Pandas

    一文详解Pandas 一.Pandas概述 二.Pandas数据结构 2.1 Series 2.2 DataFrame数据结构 二.数学与统计计算 三.DataFrame的文件操作 3.1 读取文件 ...

  9. 从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘

    本文转载自:从基础到进阶,一文详解RocketMQ事务消息,看完不会跪键盘 事务消息是RocketMQ提供的非常重要的一个特性,在4.x版本之后开源,可以利用事务消息轻松地实现分布式事务.本文对Roc ...

最新文章

  1. NFS基本配置与NFS客户端自动挂载
  2. 最大独立集 HDU 1068
  3. 小程序入门学习08--云开发01
  4. 总结几个与模块相关的命令
  5. float和position
  6. 局域网文件共享服务器软件,局域网文件夹加密工具哪个好?文件共享服务器软件介绍...
  7. 深入浅出数据分析pdf
  8. java计算机毕业设计校园流浪猫图鉴管理系统的设计与实现源码+数据库+系统+lw文档+部署
  9. 微信小程序毕业设计 就餐预约点餐小程序毕业设计
  10. Win7英文版打补丁遇到的问题汇总
  11. 微x怎么设置主题_团日活动主题策划书范文
  12. REUSE_ALV_GRID_DISPLAY_LVC函数输入参数属性的应用
  13. 视频合并软件怎么把多个视频合并为一个视频
  14. Attention原理详解
  15. C语言中的复制函数(strcpy和memcpy)
  16. 数据库原理与设计P163习题9答案
  17. 烟台大学—贺利坚的计算机课程教学链接
  18. 计算机 社会网络分析,社会网络分析的工具.ppt
  19. classes是什么意思怎么读_淦怎么读音 淦是什么意思
  20. 月历(从星期一开始)

热门文章

  1. linux 查看端口战役,漫画 :Apache Nginx80 端口争夺战
  2. 公钥 私钥_公钥 私钥 签名 验签 说的啥?
  3. java 银行卡号格式化_JS银行卡号格式化 - JavaScript常用效果 - Front-End - NalanXue's Blog...
  4. Python浅拷贝与深拷贝
  5. Ubuntu16.06LTS安装gnome-3.8桌面
  6. 2021-2025年中国冻融室行业市场供需与战略研究报告
  7. 多路径配置udev_多路径multipath配置,udev绑定
  8. arduino 按钮读取_Arduino内置教程-数字-检测按键状态
  9. 用element-ui的走马灯carousel轻松实现自适应全屏banner图 解决el-carousel默认高度300问题  组件代码
  10. Clipsync – 同步 Win 和 Android 剪贴板