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#玩多了,更喜欢这样的玩法,而对高阶组件这种方式老是感受很奇怪)

本篇文章算是本身的一次重构笔记吧,写的只是我的的一点理解,若是有更好的办法或者疏漏的地方欢迎批评指正。

相关文章
相关标签/搜索