React中的函數子組件(FaCC)和高階組件(HOC)

在接觸過React項目後,大多數人都應該已經瞭解過或則用過了HOC(High-Order-Components)和FaCC(Functions as Child Components),由於這兩個模式在大多數react的開源庫裏都存在。好比react-router裏面的withRouter 就是典型的高階組件,接受一個組件返回另一個通過加強後的組件。而react-motion中的Motion就是典型的FaCC的應用。html

HOC和FaCC二者作的事也是很是類似的,都是相似設計模式裏面的裝飾者模式。都是在原有的實例或則單元上進行功能的加強。react

固然不僅是一些開源庫中會使用,在日常的代碼編寫中,也有不少地方是適用於使用HOC和FaCC去封裝一些邏輯。好比數據埋點,新特性的toggle,獲取轉換數據等。對於加強代碼可讀性和邏輯複用來講,很是有用的。git

HOC

高階函數咱們都用過,就是接受一個函數而後返回一個通過封裝的函數:github

const plus = first => second => (first + second)
plus(1)(2) // 3

而高階組件就是高階函數的概念應用到高階組件上:express

const withClassName = ComposedComponent => props => (
   <ComposedComponent {...props} className='demo-class' />
)

// 使用
const Header = text => (<header>{text}</header>)
const headerWitheClass = withClassName(Header)

接受一個組件返回一個通過包裝的新組件。在咱們常用的withRouter就是在原有組件props上面在加上localtion等屬性。除了添加props之外高階組件還能作到:設計模式

  • 在真正調用組件先後作一些事,好比埋點數據等
  • 判斷組件是否該render,或則應該render其餘的東西,好比出錯以後render錯誤頁面
  • 傳遞props並增長新的props
  • 不render組件,轉而作一些其餘的事情,好比渲染一個外部的dom

對於上面的前三點都比較好理解,解釋一下第4點。好比你在render了一個頁面以後,須要改變一下頁面的title.這是單頁應用廣泛存在的一個需求,一般你能夠在具體router庫中使用hook去實現。固然也能夠經過HOC來實現:性能優化

const withTitleChange = ComposedComponent => {
  return class extends React.Component {
    componentDidMount () {
      const { title } = this.props
      document.title = title
    }
    render () {
      const props = this.props
      return <ComposedComponent {...props} />
    }
  }
}

FaCC

一樣FaCC也是用於加強原有組件能力的一種模式,其主要功能的實如今於react的props.children能夠是任何東西,包括函數。咱們能夠拿上面class的例子用FaCC再實現一遍:react-router

const ClassNameWrapper = ({ children }) => children('demo-class')

// 使用

const HeadWithClass = (props) => (
  <ClassNameWrapper>
    {(class) => <header classNmae={class} ></header>}
  </ClassNameWrapper>
)

在FaCC中你也能夠像HOC同樣在生命週期中作不少事對原有的組件進行封裝,基本上HOC能作的FaCC也都能作。我所在的項目以前都是大範圍的使用HOC,再通過一番討論後,開始大範圍的轉變成FaCC。app

區別

二者都是用來加強原有組件的,具體該使用那種?那種是正確的模式?社區關於這一點也有不少討論,好比就有人說FaCC是反模式:Function as Child Components Are an Anti-Pattern。他給出的理由是children並不語義化,會形成困惑,而後他提出了Component Injection的模式,有興趣的同窗能夠讀一讀。dom

具體從幾個方面作一下對比:

  1. 組合階段

組合階段意思就是HOC,FaCC和要被加強的組件的組合時候。能夠很明顯發現,FaCC對於先後組件對接依賴信息顯示的更多,相對而言更容易理解。而HOC,相互之間如何橋接,你必須得深刻到HOC內部讀代碼才能夠知道這個HOC具體幹了啥。

// HOC example
import View from './View'

const DetailPage = withServerData(withNavigator(View))
// FaCC example

import View from './View'

const DetailPage = props => (
  <FetchServerData>
    {
      data => (
        <Navigator>
          <View data={data} {...props} />
        </Navigator>
      )
    }
  </FetchServerData>
)

若是在上面再增長2個HOC,上面組合的過程就變得十分難看。而FaCC相對而言,如何封裝,數據源來自那裏,組件接受了那些數據都比較顯眼。

  1. 性能優化

在HOC中咱們能接受到宿主的prop,由於props是從HOC往下傳遞的,因此咱們也有完整的生命週期,咱們可使用shouldComponentUpdate優化。而FaCC則否則,沒法在其內部作比較props,除非在組合的時候外部在包一個組件才能進行比較props。

  1. 靈活性

FaCC 在組合階段相對HOC更爲靈活,他並不規定被加強組件如何使用它傳遞下去的屬性。而HOC基本上在編寫完後就定死了。

另外,FaCC不會再去建立一個新的Component,而HOC會建立一個新的Component而後傳遞props下去。

總結

社區中不少開源庫已經使用了兩種模式,也有不少的文章進行比較。也有不少激烈討論,固然對於最後解決問題而言,兩種模式都有好處。出於不一樣的考慮,可能選擇不同。

參考文章:

  1. http://rea.tech/functions-as-...
  2. http://rea.tech/reactjs-real-...
  3. https://medium.com/merrickchr...
  4. http://www.ituring.com.cn/boo... 第四章
相關文章
相關標籤/搜索