溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

React組件重構(gòu)之嵌套+繼承及高階組件詳解

發(fā)布時(shí)間:2020-09-22 07:58:41 來源:腳本之家 閱讀:177 作者:韓子盧 欄目:web開發(fā)

前言

在最近做的一個(gè)react項(xiàng)目中,遇到了一個(gè)比較典型的需要重構(gòu)的場(chǎng)景:提取兩個(gè)組件中共同的部分。

最開始通過使用嵌套組件和繼承的方式完成了這次重構(gòu)。

但是后來又用高階組件重新寫了一遍,發(fā)現(xiàn)更好一點(diǎn)。

在這里記錄下這兩種方式以便之后參考和演進(jìn)。

本次重構(gòu)的場(chǎng)景

因?yàn)閳?chǎng)景涉及到具體的業(yè)務(wù),所以我現(xiàn)在將它簡(jiǎn)化為一個(gè)簡(jiǎn)單的場(chǎng)景。

現(xiàn)在有兩個(gè)黑色箱子,箱子上都有一個(gè)紅色按鈕,A箱子充滿氣體,按了按鈕之后箱子里面氣體變紅,B箱子充滿泥土,按了之后箱子里面泥土變紅。

那么現(xiàn)在上一個(gè)簡(jiǎn)單的重構(gòu)前代碼:

BoxA.jsx

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

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

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

 handleShake=()=>{
 /* 搖動(dòng)后氣體沒聲音 */
 }

 render() {
 return (
  /* 這里面當(dāng)然沒有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=()=>{
 /* 搖動(dòng)后泥土有聲音 */
 }

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

使用嵌套組件進(jìn)行重構(gòu)

看看上面的代碼,即使在業(yè)務(wù)簡(jiǎn)化的情況下都有很多重復(fù)的,所以得重構(gòu)。

對(duì)于這種很明顯的箱子類問題,一般都會(huì)采用嵌套組件的方式重構(gòu)。
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=()=>{
 /* 搖動(dòng)后氣體沒聲音 */
 }

 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=()=>{
 /* 搖動(dòng)后泥土有聲音 */
 }

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

使用繼承組件的方式進(jìn)行重構(gòu)

對(duì)于很多場(chǎng)景而言,使用了嵌套組件后,可能就不需要或者沒法進(jìn)一步進(jìn)行組件提煉了。

然而完成這波操作后,我們發(fā)現(xiàn)嵌套組件BoxA和BoxB依然存在重復(fù)代碼,即按下按鈕變紅這部分代碼。

這部分代碼可以使用嵌套組件與被嵌套組件的通信機(jī)制來處理,技術(shù)上而言依然可以將這部分代碼用嵌套組件的方式來解決。

但是為了保證組件的單一職責(zé),即箱子就是個(gè)帶紅色按鈕可以搖動(dòng)的箱子,我們不知道里面以后會(huì)放什么進(jìn)去,就不能說不管以后里面放什么,只要我一按紅色按鈕,里面的物質(zhì)都會(huì)變紅。

這部分代碼肯定是不能放在嵌套組件Box里,因?yàn)樗苯硬僮髦磺短椎膬?nèi)容。

那么在這里我們可以使用繼承組件的方式。

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=()=>{
 /* 搖動(dòng)后氣體沒聲音 */
 }

 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=()=>{
 /* 搖動(dòng)后泥土有聲音 */
 }

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

通過修改后的代碼,就可以將BoxA和BoxB中相同的部分提取到BasicBox中。

這樣我們相當(dāng)于將一個(gè)功能塊提取了出來,你可以繼承BasicBox(這個(gè)命名可能不好,容易引起混淆),如果不使用state的值也完全沒有任何問題。

但是這樣做也許會(huì)帶了一些別的問題。

我們自己去看這段代碼的時(shí)候其實(shí)不難理解,不過之后讓其他人對(duì)這塊代碼做修改時(shí),后來的人就會(huì)感到奇怪,BoxA中突然間使用了一個(gè)不知道從哪里來的handleClick。

使用高階組件進(jìn)行重構(gòu)

為了解決上面的問題,后來又使用高階組件的方式玩了一遍:

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=()=>{
 /* 搖動(dòng)后氣體沒聲音 */
 }

 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=()=>{
 /* 搖動(dòng)后泥土有聲音 */
 }

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

高階組件的使用就像設(shè)計(jì)模式中的裝飾者模式(Decorator Pattern)。

總結(jié)

以上的兩種方式中,高階組件的方式對(duì)于后來者在修改上更友好一點(diǎn)。
但是用嵌套+繼承的方式理解起來其實(shí)更容易一點(diǎn),特別是去重構(gòu)一個(gè)復(fù)雜的組件時(shí),通過這種方式往往更快,拆分起來更容易。(我個(gè)人更傾向于這種,不知道是不是C#玩多了,更喜歡這樣的玩法,而對(duì)高階組件這種方式總是感覺很奇怪)
本篇文章算是自己的一次重構(gòu)筆記吧,寫的只是個(gè)人的一點(diǎn)理解,如果有更好的辦法或者疏漏的地方歡迎批評(píng)指正。

好了,以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI