React組件重構:嵌套+繼承 與 高階組件

来源:https://www.cnblogs.com/vvjiang/archive/2018/07/09/9283006.html
-Advertisement-
Play Games

前言 在最近做的一個react項目中,遇到了一個比較典型的需要重構的場景:提取兩個組件中共同的部分。 最開始通過使用嵌套組件和繼承的方式完成了這次重構。 但是後來又用高階組件重新寫了一遍,發現更好一點。 在這裡記錄下這兩種方式以便之後參考和演進。 本次重構的場景 因為場景涉及到具體的業務,所以我現在 ...


前言

在最近做的一個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#玩多了,更喜歡這樣的玩法,而對高階組件這種方式總是感覺很奇怪)

本篇文章算是自己的一次重構筆記吧,寫的只是個人的一點理解,如果有更好的辦法或者疏漏的地方歡迎批評指正。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近項目裡面遇到一些圖表需要用echarts來做。而我之前只用過一次echarts,也只是做了一個簡單的餅狀圖,並沒有涉及到很多的配置。但是現在這個項目,這些圖表需要自己配置很多東西。像什麼多餘的網格線不顯示啊,每個柱子的不一樣的顏色漸變啊,這些都還好。問題在一個頁面有多個柱狀圖,而這些柱狀圖除了數 ...
  • CSS3實現文字摺紙效果 效果圖: 代碼如下,複製即可使用: 如有錯誤,歡迎聯繫我改正,非常感謝!!! ...
  • var allMyApp = [ {"startDate": "2018-07-07 12:30:00",'name':'aa'}, {"startDate": "2018-07-09 12:30:00",'name':'bb'}, {"startDate": "2018-07-05 12:30:0... ...
  • 使用Three.js在網上中進行3D圖形的展示 Three.js的官網 https://threejs.org/ 第一個Demo,生成一個旋轉的正方體 ...
  • 基線、底線、頂線、中線、vertical-align 頂線:中文漢字的的上端沿; 中線:橫過英文字母x的中間的線; 基線(base line)並不是漢字文字的下端沿,而是英文字母“x”的下端沿; 底線:中文漢字的下端沿; 內容區:指底線和頂線包裹的區域(行內元素display:inline可以通過b ...
  • 參考鏈接:https://blog.csdn.net/liuwengai/article/details/78987957 該實現方法是根據上面的鏈接改編為小程式的實現,代碼如下: wxml: wxss: js: 實現效果如圖: 提醒:預設textarea應該是200個字,如果想要增加字數限制,使用 ...
  • 都是網上找的 整理一下 一起學習 方便以後好用 感謝各位大佬的分享! ...
  • 1)日期轉換為毫秒 如果格式是:yyyy/mm/dd hh:mm:ss可以直接轉換。var oldTime = (new Date("2018/07/09 14:13:11")).getTime(); //得到毫秒數 如果日期格式是:yyyy-mm-dd hh:mm:ss需要轉化格式 var sta ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...