react 高階函數詳解(附送彩蛋)

此文適合react新手入門,react大佬能夠略過(畢竟之前都是寫vue,React才寫了一個多月, 掩面淚奔)html

主要是學習react中的一些經驗總結,若是你以爲對你有幫助,能夠給個贊githubvue

react項目入門react

react版本:16.0.0 (由於工做中仍是15的版本)git

首先咱們先來講說 有狀態組件和無狀態組件github

有狀態組件 無狀態組件

有狀態組件:組件內部狀態發生變化,須要state來保存變化。redux

無狀態組件:組件內部狀態是不變的,用不到state。建議寫成函數組件後端

組件設計思路:經過定義少部分有狀態組件管理整個應用的狀態變化,而且將狀態經過props傳遞給其他無狀態組件。 設計模式

有狀態組件主要關注處理狀態變化的業務邏輯,無狀態組件主要關注組件UI渲染工做。這樣更有利於組件的複用,組件之間解耦更加完全數組

這樣有時就會產生一個問題,若是給UI組件加不一樣的邏輯怎麼辦?bash

2種比較好的方法, this.props.children 和 HOC , 具體實例。那麼下面就來詳細說說 HOC 高階組件

知識前置:

裝飾器設計模式:容許向一個現有的對象添加新的功能,同時又不改變其結構。這種類型的設計模式屬於結構型模式,它是做爲現有的類的一個包裝。

這種模式建立了一個裝飾類,用來包裝原有的類,並在保持類方法簽名完整性的前提下,提供了額外的功能。

HOC 高階組件 (封裝並分離組件的通用邏輯。其實就是裝飾器設計模式的應用)

  • 基本概念:

在JS中高階函數能夠接受一個函數做爲參數,返回值做爲也是函數的函數。相似的 高階組件也能夠接受一個組件爲參數,返回一個被加工過的組件。

本質上高階函數是一個函數,而不是組件。

  • 使用場景:
  1. 操縱props
  2. 經過ref訪問組件的實例
  3. 組件狀態提高
  4. 用其餘元素包裝組件

例子:

  1. 操縱props 這個用的比較多

在被包裝的組件接收到props以前。高階組件能夠先攔截到props,對props執行增刪改等操做,而後將修改過的props傳給被包裝組件。

import React, { Component } from 'react'

function withPersistentData (wrapedComponent) {
  return class extends Component {
    componentWillMount() {
      let data = localStore.getItem('data');
      this.setState({data})
    }

    render () {
      const { data } = this.state;
      // 經過{...this.props} 把傳給當前組件的值繼續傳給被包裝的組件
      return <wrapedComponent data={data} {...this.props} />
    }
  }
}

@withPersistentData
export default class myComponent extends Component {
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    )
  }
}

複製代碼
  1. 經過ref訪問組件的實例。這個用法我是比較少用到。

高階組件經過ref獲取被包裝的組件實例的引用,而後高階組件就具有了直接操做被包裝組件的屬性和方法的能力。

function withRef (wrapedComponent) {
  return class extends Component {
    someMethod = () => {
      // this.wrapedComp        被包裝組件實例 
      // someMethodWrapedComp   被包裝組件的方法
      this.wrapedComp.someMethodWrapedComp()
    }

    render () {
      // 經過{...this.props} 把傳給當前組件的值繼續傳給被包裝的組件
      // 給被包裝的組件添加ref屬性,獲取被包裝組件實例並賦值給this.wrapedComp
      return <wrapedComponent ref={(comp) =>{this.wrapedComp = comp}} {...this.props} />
    }
  }
}
複製代碼
  1. 組件狀態提高

上面已經說過 無狀態組件更容易被複用,咱們能夠利用高階組件將本來受控組件中須要本身維護的的狀態統一提高到高階組件中,受控組件無狀態化。

import React, { Component } from 'react'

function withControlledComp(wrappedComp) {
  state = {
    value : null,
  }
  handleValueChange = (e) => {
    this,setState({value: e.target.value})
  }

  render () {
    const newProps ={
      controlledProps: {
        value: this.state.value,
        onChange: this.handleValueChange
      }
    }
    return <wrappedComp {...this.props} {...newProps} />
  }
}

@withControlledComp
class ControlledComp extends Component {
  render () {
    // 此時的受控組件爲無狀態組件,狀態由高階組件控制
    return <input {...this.props.controlledProps} />
  }
}
複製代碼
  1. 用其餘元素包裝組件
function withRedColor (wrapedComponent) {
  return class extends Component {
    render () {
      return (<div style={color: 'red}><wrapedComponent {...this.props} /> </div>) } } } 複製代碼



* 參數傳遞 高階組件的參數除了接受組件,還能夠接受其餘參數。

在第一個操做props的例子裏,若是要獲取key值不肯定時,這個組件就不知足了。

咱們通常採用這種方式:HOC(...params)(wrappedComp)

function withPersistentData = (key) => (wrapedComponent) => {
  return class extends Component {
    componentWillMount() {
      let data = localStore.getItem(key);
      this.setState({data})
    }

    render () {
      const { data } = this.state;
      // 經過{...this.props} 把傳給當前組件的值繼續傳給被包裝的組件
      return <wrapedComponent data={data} {...this.props} />
    }
  }
}

class myComponent extends Component {
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    )
  }
}

// 獲取key=‘data’的數據
const myComponentWithData = withPersistentData('data')(myComponent)

// 獲取key=‘name’的數據
const myComponentWithData = withPersistentData('name')(myComponent)
複製代碼

實際上這種形式的高階組件大量出如今第三方的庫中,例如react-redux中的connect函數

connect(mapStateToProps, mapDispatchToProps)(wrappedComponent)
複製代碼



* 注意事項
  1. 不要在render中使用高階組件,也儘可能不要在其餘的生命週期函數中使用高階組件。 由於高階組件每次返回的都是一個新組件,因而每次render,前一次建立的組件都會被卸載,本次建立的組件會被從新掛載。
  2. 若是須要使用被包裝組件的靜態方法,就必需要手動複製這些方法。由於高階組件不包含被包裝組件的靜態方法。
  3. Refs不回被傳遞給被包裝組件。
  4. 與父組件的區別。若是這部分邏輯與UI/DOM相關,那麼這部門邏輯適合放在父組件中實現;若是邏輯與DOM不直接相關,那麼這部分邏輯適合放在高階組件的抽象中。例如數據校驗請求發送等

彩蛋來了,彩蛋就是react生命週期的使用場景,哈哈哈哈哈,驚不驚喜,意不意外。

react組件 的生命週期 以及 使用場景

掛載階段

1. constructor
2. componentWillMount
3. render
4. componentDidMount
複製代碼

使用場景

1. coustructor  
  一般用於初始化組件的state以及綁定事件的處理方法(好比bind(this))等

2. componentWiillMound
  在組件被掛載到DOM前調用,且只會調用一次,
  實際項目中比較少用到,由於能夠在該方法中的執行的均可以提到coustructor中
  在這個方法中this.setState不會引發從新渲染

3. render
  渲染方法。
  注意:render只是返回一個UI的描述,真正渲染出頁面DOM的工做由react本身完成
  
4. componentDidMount
  在組件被掛載到DOM後調用,且只會調用一次,
  一般用於像後端請求數據
  在這個方法中this.setState會引發組件的從新渲染

複製代碼

更新階段

1. componentWillReceiveProps
2. shouldComponentUpdate
3. componentWillUpdate
4. render
5. componentDidUpdate
複製代碼

使用場景

1. componentWillRceiveProps(nextProps)
  這個方法只在props引發組件更新時調用。
  通常會比較一下this.props和nextProps來決定是否執行props變化後的邏輯
  好比:根據新的props調用this.setState來觸發組件的從新渲染

2. shouldComponentUpdate(nextProps,nextState)
  這個方法決定組件是否繼續執行更新過程。 
  默認是true,繼續更新;false阻止更新。
  通常是經過比較nextPops,nextState和當前組件的props,state來決定返回結果。 
  這個方法能夠減小沒必要要的渲染,優化組件性能。
  緣由:根據渲染流程,首先會判斷shouldComponentUpdate是否須要更新。若是須要更新,調用render方法生成新的虛擬DOM與舊的虛擬DOM進行對比(render只是返回一個UI描述),若是對比不一致,則根據最小粒度改變去更新DOM。

3. componentWillUpdate
  render前調用,組件更新前執行某些邏輯的地方。
  通常不多用到。

4. render

5. componentDidUpdate(prevProps, prevState)
  組件更新後被調用,能夠做爲操做更新後DOM的地方。
  這個方法中的prevProps和prevState表明組件中更新前的props和state。

注意:在render前的生命週期中,componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate中的this.state依然指向更新前的state。
複製代碼

銷燬階段

componentWillUnmount
  組件卸載前被調用。
  清除定時器,清除componentDidMount中手動建立的DOM,取消請求等
複製代碼

結束語

最後感謝能看到這裏的朋友,由於水平有限,若是有錯誤敬請指正,十分感激。

參考:
react進階之路

相關文章
相關標籤/搜索