React组件重构之嵌套+继承及高阶组件详解


Posted in Javascript onJuly 19, 2018

前言

在最近做的一个react项目中,遇到了一个比较典型的需要重构的场景:提取两个组件中共同的部分。

最开始通过使用嵌套组件和继承的方式完成了这次重构。

但是后来又用高阶组件重新写了一遍,发现更好一点。

在这里记录下这两种方式以便之后参考和演进。

本次重构的场景

因为场景涉及到具体的业务,所以我现在将它简化为一个简单的场景。

现在有两个黑色箱子,箱子上都有一个红色按钮,A箱子充满气体,按了按钮之后箱子里面气体变红,B箱子充满泥土,按了之后箱子里面泥土变红。

那么现在上一个简单的重构前代码:

BoxA.jsx

import React, { Component, PropTypes } from 'react'

class BoxA extends Component {
 state={
 color:'black'
 }

 handleClick=()=>{
 this.setState({
  color:'red'
 })
 }

 handleShake=()=>{
 /* 摇动后气体没声音 */
 }

 render() {
 return (
  /* 这里面当然没有onShake这种事件,理解意思就行了 */
  <div style={{backgroundColor:'black'}} onShake={this.handleShake}>
   <button onClick={this.handleClick} style={{backgroundColor:'red'}}></button>
   <div>
   /* 气体组件,没毛病 */
   <气体 color={this.state.color} />
   </div>
  </div>
 )
 }
}

BoxB.jsx

import React, { Component, PropTypes } from 'react'

class BoxB extends Component {
 state={
 color:'black'
 }
 handleClick=()=>{
 this.setState({
  color:'red'
 })
 }

 handleShake=()=>{
 /* 摇动后泥土有声音 */
 }

 render() {
 return (
  <div style={{backgroundColor:'black'}} onShake={this.handleShake}>
   <button onClick={this.handleClick} style={{backgroundColor:'red'}}></button>
   <div>
   <泥土 color={this.state.color} />
   </div>
  </div>
 )
 }
}

使用嵌套组件进行重构

看看上面的代码,即使在业务简化的情况下都有很多重复的,所以得重构。

对于这种很明显的箱子类问题,一般都会采用嵌套组件的方式重构。
Box.jsx

import React, { Component, PropTypes } from 'react'

class Box extends Component {

 static propTypes = {
 children: PropTypes.node,
 onClick: PropTypes.func,
 onShake: PropTypes.func
 }

 render() {
 return (
  <div style={{backgroundColor:'black'}} onShake={this.props.onShake}>
   <button onClick={this.props.onClick} style={{backgroundColor:'red'}}></button>
   <div>
   {this.children}
   </div>
  </div>
 )
 }
}

BoxA.jsx

import React, { Component, PropTypes } from 'react'
import Box from './Box.jsx'

class BoxA extends Component {
 state={
 color:'black'
 }

 handleClick=()=>{
 this.setState({
  color:'red'
 })
 }

 handleShake=()=>{
 /* 摇动后气体没声音 */
 }

 render() {
 return (
  <Box onClick={this.handleClick} onShake={this.props.handleShake}>
  <气体 color={this.state.color} />
  </Box>
 )
 }
}

BoxB.jsx

import React, { Component, PropTypes } from 'react'

class BoxB extends Component {
 state={
 color:'black'
 }
 handleClick=()=>{
 this.setState({
  color:'red'
 })
 }

 handleShake=()=>{
 /* 摇动后泥土有声音 */
 }

 render() {
 return (
  <Box onClick={this.handleClick} onShake={this.props.handleShake}>
  <泥土 color={this.state.color} />
  </Box>
 )
 }
}

使用继承组件的方式进行重构

对于很多场景而言,使用了嵌套组件后,可能就不需要或者没法进一步进行组件提炼了。

然而完成这波操作后,我们发现嵌套组件BoxA和BoxB依然存在重复代码,即按下按钮变红这部分代码。

这部分代码可以使用嵌套组件与被嵌套组件的通信机制来处理,技术上而言依然可以将这部分代码用嵌套组件的方式来解决。

但是为了保证组件的单一职责,即箱子就是个带红色按钮可以摇动的箱子,我们不知道里面以后会放什么进去,就不能说不管以后里面放什么,只要我一按红色按钮,里面的物质都会变红。

这部分代码肯定是不能放在嵌套组件Box里,因为它直接操作着被嵌套的内容。

那么在这里我们可以使用继承组件的方式。

Box.jsx

import React, { Component, PropTypes } from 'react'

class Box extends Component {
 static propTypes = {
 children: PropTypes.node,
 onClick: PropTypes.func,
 onShake: PropTypes.func
 }

 render() {
 return (
  <div style={{backgroundColor:'black'}} onShake={this.props.onShake}>
   <button onClick={this.props.onClick} style={{backgroundColor:'red'}}></button>
   <div>
   {this.children}
   </div>
  </div>
 )
 }
}

BasicBox.jsx

import React, { Component, PropTypes } from 'react'
class BasicBox extends Component {
 state={
 color:'black'
 }

 handleClick=()=>{
 this.setState({
  color:'red'
 })
 }
}

BoxA.jsx

import React, { Component, PropTypes } from 'react'
import Box from './Box.jsx'

class BoxA extends BasicBox {
 handleShake=()=>{
 /* 摇动后气体没声音 */
 }

 render() {
 return (
  <Box onClick={this.handleClick} onShake={this.props.handleShake}>
  <气体 color={this.state.color} />
  </Box>
 )
 }
}

BoxB.jsx

import React, { Component, PropTypes } from 'react'

class BoxB extends BasicBox {
 handleShake=()=>{
 /* 摇动后泥土有声音 */
 }

 render() {
 return (
  <Box onClick={this.handleClick} onShake={this.props.handleShake}>
  <泥土 color={this.state.color} />
  </Box>
 )
 }
}

通过修改后的代码,就可以将BoxA和BoxB中相同的部分提取到BasicBox中。

这样我们相当于将一个功能块提取了出来,你可以继承BasicBox(这个命名可能不好,容易引起混淆),如果不使用state的值也完全没有任何问题。

但是这样做也许会带了一些别的问题。

我们自己去看这段代码的时候其实不难理解,不过之后让其他人对这块代码做修改时,后来的人就会感到奇怪,BoxA中突然间使用了一个不知道从哪里来的handleClick。

使用高阶组件进行重构

为了解决上面的问题,后来又使用高阶组件的方式玩了一遍:

hocBox.jsx

import React, { Component, PropTypes } from 'react'

hocBox=(WrappedComponent)=>{
 return class Box extends Component{
  static propTypes = {
  onShake: PropTypes.func
  }

  state={
  color:'black'
  }

  handleClick=()=>{
  this.setState({
   color:'red'
  })
  }

  render() {
  return (
   <div style={{backgroundColor:'black'}} onShake={this.props.handleShake}>
    <button onClick={this.handleClick} style={{backgroundColor:'red'}}></button>
    <div>
    <WrappedComponent color={this.state.color} />
    </div>
   </div>
  )
  }
 }
}

BoxA.jsx

import React, { Component, PropTypes } from 'react'
import Box from './hocBox.jsx'


const 气体WithBtnBox=hocBox(气体)
class BoxA extends BasicBox {
 handleShake=()=>{
 /* 摇动后气体没声音 */
 }

 render() {
 return (
  <气体WithBtnBox onShake={this.handleShake} />
 )
 }
}

BoxB.jsx

import React, { Component, PropTypes } from 'react'
import Box from './hocBox.jsx'

const 泥土WithBtnBox=hocBox(泥土)
class BoxA extends BasicBox {
 handleShake=()=>{
 /* 摇动后泥土有声音 */
 }

 render() {
 return (
  <泥土WithBtnBox onShake={this.handleShake} />
 )
 }
}

高阶组件的使用就像设计模式中的装饰者模式(Decorator Pattern)。

总结

以上的两种方式中,高阶组件的方式对于后来者在修改上更友好一点。
但是用嵌套+继承的方式理解起来其实更容易一点,特别是去重构一个复杂的组件时,通过这种方式往往更快,拆分起来更容易。(我个人更倾向于这种,不知道是不是C#玩多了,更喜欢这样的玩法,而对高阶组件这种方式总是感觉很奇怪)
本篇文章算是自己的一次重构笔记吧,写的只是个人的一点理解,如果有更好的办法或者疏漏的地方欢迎批评指正。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
基于jquery的网站幻灯片切换效果焦点图代码
Sep 15 Javascript
JQuery实现鼠标移动到图片上显示边框效果
Jan 09 Javascript
jQuery 鼠标经过(hover)事件的延时处理示例
Apr 14 Javascript
js限制checkbox选中个数以限制六个为例
Jul 15 Javascript
js限制文本框的输入内容代码分享(3类)
Aug 20 Javascript
微信小程序中用WebStorm使用LESS
Mar 08 Javascript
微信小程序实现滚动消息通知
Feb 02 Javascript
解决vue点击控制单个样式的问题
Sep 05 Javascript
JS删除String里某个字符的方法
Jan 06 Javascript
开源一个微信小程序仪表盘组件过程解析
Jul 30 Javascript
jquery使用echarts实现有向图可视化功能示例
Nov 25 jQuery
JS实现放烟花效果
Mar 10 Javascript
微信小程序实现折叠展开效果
Jul 19 #Javascript
详解Angularjs 自定义指令中的数据绑定
Jul 19 #Javascript
微信小程序实现天气预报功能
Jul 18 #Javascript
vue代理和跨域问题的解决
Jul 18 #Javascript
小程序自定义组件实现城市选择功能
Jul 18 #Javascript
微信小程序实践之动态控制组件的显示/隐藏功能
Jul 18 #Javascript
微信小程序项目实践之主页tab选项实现
Jul 18 #Javascript
You might like
解析php中的escape函数
2013/06/29 PHP
zf框架的校验器使用使用示例(自定义校验器和校验器链)
2014/03/13 PHP
3种方法轻松处理php开发中emoji表情的问题
2016/07/18 PHP
使用SyntaxHighlighter实现HTML高亮显示代码的方法
2010/02/04 Javascript
window.open以post方式将内容提交到新窗口
2012/12/26 Javascript
推荐一个自己用的封装好的javascript插件
2015/01/29 Javascript
JavaScript的Backbone.js框架的一些使用建议整理
2016/02/14 Javascript
jQuery实现iframe父窗体和子窗体的相互调用
2016/06/17 Javascript
关于JavaScript 原型链的一点个人理解
2016/07/31 Javascript
jQuery图片切换动画效果
2017/02/28 Javascript
JS控件bootstrap datepicker使用方法详解
2017/03/25 Javascript
原生JS实现九宫格抽奖效果
2017/04/01 Javascript
Vue.js实战之使用Vuex + axios发送请求详解
2017/04/04 Javascript
Javascript刷新页面的实例
2017/09/23 Javascript
基于vue配置axios的方法步骤
2017/11/09 Javascript
Vuejs 单文件组件实例详解
2018/02/09 Javascript
Node.js 使用AngularJS的方法示例
2018/05/11 Javascript
vue-router传递参数的几种方式实例详解
2018/11/13 Javascript
javascript实现手动点赞效果
2019/04/09 Javascript
深入学习JavaScript 高阶函数
2019/06/11 Javascript
如何在wxml中直接写js代码(wxs)
2019/11/14 Javascript
JavaScript使用百度ECharts插件绘制饼图操作示例
2019/11/26 Javascript
Vue filter 过滤当前时间 实现实时更新效果
2019/12/20 Javascript
vue-以文件流-blob-的形式-下载-导出文件操作
2020/08/07 Javascript
vue点击Dashboard不同内容 跳转到同一表格的实例
2020/11/13 Javascript
Python面向对象编程中关于类和方法的学习笔记
2016/06/30 Python
Python编程实现输入某年某月某日计算出这一天是该年第几天的方法
2017/04/18 Python
python3获取当前目录的实现方法
2019/07/29 Python
Pycharm 字体大小调整设置的方法实现
2019/09/27 Python
Python爬虫之爬取淘女郎照片示例详解
2020/07/28 Python
python判断一个变量是否已经设置的方法
2020/08/13 Python
Pytorch 扩展Tensor维度、压缩Tensor维度的方法
2020/09/09 Python
利用html5 canvas动态画饼状图的示例代码
2018/04/02 HTML / CSS
苹果Mac升级:MacSales.com
2017/11/20 全球购物
小学优秀班主任事迹材料
2014/05/17 职场文书
纪委立案决定书
2015/06/24 职场文书