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

前言

在最近作的一個react項目中,遇到了一個比較典型的須要重構的場景:提取兩個組件中共同的部分。node

最開始經過使用嵌套組件和繼承的方式完成了此次重構。react

可是後來又用高階組件從新寫了一遍,發現更好一點。設計模式

在這裏記錄下這兩種方式以便以後參考和演進。app

本次重構的場景

由於場景涉及到具體的業務,因此我如今將它簡化爲一個簡單的場景。this

如今有兩個黑色箱子,箱子上都有一個紅色按鈕,A箱子充滿氣體,按了按鈕以後箱子裏面氣體變紅,B箱子充滿泥土,按了以後箱子裏面泥土變紅。設計

那麼如今上一個簡單的重構前代碼:code

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>
    )
  }
}

使用嵌套組件進行重構

看看上面的代碼,即便在業務簡化的狀況下都有不少重複的,因此得重構。jsx

對於這種很明顯的箱子類問題,通常都會採用嵌套組件的方式重構。

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#玩多了,更喜歡這樣的玩法,而對高階組件這種方式老是感受很奇怪)

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

相關文章
相關標籤/搜索