React系列之一塊兒認識Render Prop

1.mixins

寫過react項目的應該都碰到過,不一樣組件複用相同代碼的問題,在react早期使用React.createClass建立組件的時代,咱們常用的是mixins來實現代碼複用。好比有個組件A,它用來實時的獲取鼠標的位置。react

//A組件
import React from 'react'
import ReactDOM from 'react-dom'

const App = React.createClass({
  getInitialState() {
    return { x: 0, y: 0 }
  },

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

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

若是此時有個組件B也想集成這個功能,咱們能夠經過mixins,代碼是這樣的git

//B組件
import React from 'react'
import ReactDOM from 'react-dom'

const MouseMixin = {
  getInitialState() {
    return { x: 0, y: 0 }
  },

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

const App = React.createClass({
  // Use the 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'))

很容易是吧~但委屈的是react16以後就再也不支持mixins了,由於es6普及了呀!es6


那怎麼辦呢?辦法老是有的,HOC(高階組件)的概念被提出來,什麼是高階組件?說白了其實就是把函數當作參數傳入到另外一個函數中,在咱們展開高階組件前,咱們先來想一想除了由於es6的普及react再也不支持mixins以外,mixins還有啥其它缺點不。固然是有的,有如下幾點:github

  1. 難以溯源,mixins修改state,在組件內部你沒法知道state從哪來,尤爲是有多個mixins的狀況。
  2. 命名空間,多個mixins修改同一個state致使的命名衝突。

2.HOC(高階組件)

因此爲了代替mixins,不少人就提出了HOC(高階組件),代碼是下面這樣的。react-router

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

const withMouse = (Component) => {
  return class extends React.Component {
    state = { x: 0, y: 0 }

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

    render() {
      return (
        <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
          <Component {...this.props} mouse={this.state}/>
        </div>
      )
    }
  }
}

class App extends React.Component{
    render() {
      // 代替直接處理state,咱們從props裏得到x,y座標
      const { x, y } = this.props.mouse

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

//把App組件當作參數傳到withMouse方法裏面,在withMouse內部經過props得到x、y座標值
const AppWithMouse = withMouse(App)

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

看起來很不錯的樣子!app


可是,回到以前mixins存在的問題,咱們想想,HOC有上述的問題麼?咱們來看下:<br/>dom

  1. 難以溯源,跟mixins不一樣的是,咱們再也不糾結state的源頭,咱們如今要糾結的是HOC的props裏提供了些啥...
  2. 命名空間的衝突,這個問題依然存在,props裏屬性名可能會被多個HOC重複使用。

個人天.....函數

3.Render Prop

幸運女神降臨!
mmp,前面都是炮灰,到了劃重點的時候啦!<br/>學習

Render Prop是個值爲函數的屬性,經過Render Prop,組件知道什麼應該被渲染

很糊塗是否是,看代碼ui

import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'

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({
  render() {
    return (
      <div style={{ height: '100%' }}>
        <Mouse render={({ x, y }) => (
          <h1>The mouse position is ({x}, {y})</h1>
        )}/>
      </div>
    )
  }
})

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

看明白了麼,這裏咱們經過定義一個render屬性,值是個函數,描述了咱們想要渲染的元素,而後在子組件裏面調用該render方法,再回頭看下以前的兩個問題,難以溯源,如今主動權在父組件上,我要什麼數據大家給我拿來就好了,大家子組件各自去實現,我只要結果不要過程,於是就不存在數據來源問題,命名空間的問題也沒了。好厲害~~~。
最後偷偷的告訴大家一個更厲害的,上面的render方法裏面咱們是直接寫出了渲染x,y值,只適用於當前App組件,咱們能夠經過高階組件來達到爲任何組件添加該功能,代碼是這樣的。

const withMouse = (Component) => {
    return class extends React.Component{
        render() {
            return <Mouse render={mouse=>(
                <Component {...this.props} mouse={mouse}/>
            )}/>
        }
    }
}

聽說react-router源碼裏面爲每一個組件增長路由屬性就是經過該方法!

好了!大功完成了,歡迎一塊兒討論學習~

我的博客地址:意卿

相關文章
相關標籤/搜索