React的演变--逻辑复用

React编程中的一个核心思想就是组件化,通过对状态和逻辑的封装,提高组件的复用性。组件作为一个独立的解耦模块,可以有效降低测试难度。从React诞生到发展至今,关于逻辑复用的方案,社区和官方都给出了很多不同的方案。这里总结一下,在React中,代码复用的几种方案及其各自的利弊:

Mixin

了解过javascript设计模式的同学,可能对这个词语会比较熟悉,Mixin其实就是混入的方式,将功能注入到原型对象中,来实现代码和逻辑的复用。但是副作用也是很明显的,他会对原有的对象造成污染,函数来源不确定,并且可能出现命名冲突的问题。

对于刚接触react开发不久的同学,可能都没有听说过这个方案,但是在React比较早期的版本,大概在15年那会,mixin方案还是很流行的。

我们通过一个简单的例子,看一下在React中,如何使用mixin来实现逻辑复用。

比如鼠标位置追踪:

import React from 'react'
import ReactDOM from 'react-dom'

// mixin 中含有了你需要在任何应用中追踪鼠标位置的样板代码。
// 我们可以将样板代码放入到一个 mixin 中,这样其他组件就能共享这些代码
const MouseMixin = {
  getInitialState() {
    return { x: 0, y: 0 }
  },

  handleMouseMove(event) {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  }
}

const App = React.createClass({
  // 使用 mixin!
  mixins: [ MouseMixin ],
  
  render() {
    const { x, y } = this.state

    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        <h1>The mouse position is ({x}, {y})</h1>
      </div>
    )
  }
})

ReactDOM.render(<App/>, document.getElementById('app'))

我们创建了一个MouseMixin,然后在react.createClass 这个范式中,将该mixin引入,这样任何组件都可以复用该逻辑,追踪鼠标的位置。

但是细心观察可以发现,这种方式其实有很多的弊端:

  • 他打破了原有组件的封装
  • 可能会出现命名冲突的问题
  • 增加复杂度
  • ES6 不支持

HOC

随着ES6 Class 的到来,React团队最终决定使用ES6 Class代替原有的createClass。既然mixin有这么多的弊端,并且ES6不支持,那么新的复用方案呼之欲出,那就是HOC.

所谓HOC,就是指高阶组件,他的概念类似于高阶函数,只是这里接受一个组件作为参数,经过装饰之后,返回一个新的组件。装饰的过程,就是将可复用的逻辑,附加到原有的组件上,可以是组件结构的扩展,也可以是功能的扩充。

HOC模式在抽离公用逻辑的同时,减少对原有组件的侵入性,非常适合在开发过程中业务模块的封装与复用。但是他同样会有一些隐藏的问题:

  • 命名冲突。 HOC通过props向被装饰的组件传递数据,如果多个HOC装饰同一个组件的话,就可能出现命名冲突
  • 不够直接。我们从props中接受的数据,有时候不知道是从那个HOC传递过来的。虽然语义化的命名可能会解决这个问题,但是让规约来弥补设计方案的缺陷,本身就是不太合理的。

虽然HOC存在一些以上隐藏的问题,但是社区以及平时的业务开发中,还是很流行的。

另外,HOC和mixin两者都存在的一个问题是,他们都是静态组合,而非动态组合。在类被创建的时候,HOC和mixin就生效了,而非是在运行态。一会儿我将看一下,render props是如何实现动态组合的

Render Props

Render Props是一个类型为函数的props,来实现组件之间的代码复用。
使用该方案解决上述问题的代码如下:

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
// 与 HOC 不同,我们可以使用具有 render prop 的普通组件来共享代码
class Mouse extends React.Component {
  static propTypes = {
    render: PropTypes.func.isRequired
  }
  state = { x: 0, y: 0 }
  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    })
  }
  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    )
  }
}
const App = React.createClass({
  renderTitle =({x, y})=>{
	return 
      // render prop 给了我们所需要的 state 来渲染我们想要的
      <h1>The mouse position is ({x}, {y})</h1>
  }
  render() {
    return (
      <div style={{ height: '100%' }}>
        <Mouse render={this.renderTitle}/>
      </div>
    )
  }
})

Mouse 组件接受一个类型为函数的render props,然后在render中调用该方法,并将自身的state作为参数,这样,其他组件就能到Mouse的状态,并渲染想要的内容。并且我们注意到,组合是发生在render中的,也就是说在运行态下的动态组合,我们就可以用到原组件内部的任何state 或 props数据,以及生命周期函数等等。

该方案也规避了上述提到的其他缺陷。

Hooks

Hooks是React官方提出的一个全新的概念,旨在解决React开发过程中遇到的一系列问题,其中就包含上面提到的逻辑复用。关于该问题,官方文档中提出,之前你可以会用HOC或者 render props来试图解决,但是这种模式需要对组件进行重组,未免显得有点麻烦,并且会造成组件的嵌套深渊,让你在调试过程中难以追踪。

Hooks可以帮助你将状态逻辑从组件中抽离出来,在不需要改变组件DOM结构层次的情况下,实现逻辑复用。

如果采用Hooks的话,代码实现如下:

aaa
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值