幾種 react 組件的代碼複用(mixin-HOC-render props -Hooks)

文章的原文連接github.com/RachelRen/b…html

mixin

Mixin 設計模式

Mixin(混入)是一種經過擴展收集功能的方式,它本質上是將一個對象的屬性拷貝到另外一個對象上面去,能夠拷貝多個屬性到一個對象上,爲了解決代碼複用問題。react

經常使用的方法:JQuery 的 extend 方法。git

var LogMixin = {
  log: function() {
    console.log('log');
  },
  componentDidMount: function() {
    console.log('in');
  },
  componentWillUnmount: function() {
    console.log('out');
  }
};

var User = React.createClass({
  mixins: [LogMixin],
  render: function() {
    return (<div>...</div>)
  }
});

var Goods = React.createClass({
  mixins: [LogMixin],
  render: function() {
    return (<div>...</div>)
  }
});


複製代碼

 缺點

  1. Mixin 可能會相互依賴,相互耦合,不利於代碼維護
  2. 不一樣的 Mixin 中的方法可能會相互衝突
  3. 如今大量使用 ES6 語法後,React.createClass 已經取消,這種方式也再也不推薦

高階組件(HOC)

高階組件的定義:github

高階組件(HOC)是 React 中用於複用組件邏輯的一種高級技巧。HOC 自身不是 React API 的一部分,它是一種基於 React 的組合特性而造成的設計模式。 (高階組件是參數爲組件,返回值爲新組件的函數。)設計模式

具體的意思就是: 高階組件能夠看做 React 對裝飾模式的一種實現,高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。他會返回一個加強的 React 組件。高階組件可讓咱們的代碼更具備複用性,邏輯性與抽象性,能夠對 render 方法進行劫持,也能夠控制 props 與 state。bash

const EnhancedComponent = higherOrderComponent(WrappedComponent);
複製代碼

實現高階組件的兩種方式:app

  1. 屬性代理: 高階組件經過被包裹的 React 組件來操做 props
  2. 反向繼承:高階組件繼承於被包裹的 React 組件。

屬性代理

import React,{Component} from 'react';

const HOC = (WrappedComponent) =>
  class WrapperComponent extends Component {
    state = { number: 0 };
    btnClick = () => {
        this.setState({
            number: this.state.number++
        })
    }
    render() {
        const newProps = {
            btnClick: this.btnClick,
            number: this.state.number
        }
        return (
            <div>
                rere HOC
                <WrappedComponent {...this.props} {...this.newProps} />
            </div>
        )
    }
}

export default HOC;

class MyComponent extends Component{
    //...
}

export default HOC(MyComponent)
複製代碼

這裏最重要的部分是render 方法中返回的 WrappedComponent 的 React 組件,這樣就能夠經過高階組件來傳遞 props,這就是屬性代理。異步

這樣組件就能夠一層層地做爲參數被調用,原始組件就具有了高階組件對它的修飾,也保持了單個組件的封裝性,與易用性。ide

特色

  1. 控制 props 咱們能夠在 HOC 裏面對 props 進行增刪查改等操做函數

    const MouseHoc = (MouseComponent, props) => {
        props.text = props.text + "---I can operate props";
        return class extends React.Component {
            render() {
                return (
                    <div style={{ height: "100%" }} onMouseMove={this.handleMouseMove}>
                        <MouseComponent {...props} mouse={this.state} />
                    </div>
                );
            }
        };
    };
    MouseHoc(Mouse, {
        text: "some thing..."
    });
    複製代碼
  1. 經過 refs 使用引用

    function refHOC(WrappedComponent) {
         return class extends Component {
             componentDidMount() {
                 this.wapperRef.log();
             }
             render() {
                 return (
                     <WrappedComponent
                         {...this.props}
                         ref={ref => {
                             this.wapperRef = ref;
                         }}
                     />
                 );
             }
         };
     }
    複製代碼
  2. 抽象 state

  3. 渲染劫持 高階組件能夠在 render 函數中作很是多的操做,從而控制原組件的渲染輸出。只要改變了原組件的渲染,咱們都將它稱之爲一種渲染劫持。

    function visibleHOC(WrappedComponent) {
        return class extends Component {
            render() {
                if (this.props.visible === false) return null;
                return <WrappedComponent {...props} />;
            }
        };
    }
    複製代碼
    複製代碼
  1. 使用其餘元素包裹 WrappedCompoennt 修改 props

反向繼承

const MyContainer = (WrappedComponent) =>
    class extends WrappedComponent{
        render(){
            return super.render();
        }
    }
複製代碼

這裏返回的組件繼承了 WrappedComponent,由於被動地繼承了 WrappedComponent,全部的調用都會反向。

注意事項

  1. 當咱們應用 HOC 去加強另外一個組件時,咱們實際使用的組件已經不是原組件了,因此咱們拿不到原組件的任何靜態屬性,咱們能夠在 HOC 的結尾手動拷貝他們

    function proxyHOC(WrappedComponent) {
        class HOCComponent extends Component {
            render() {
            return <WrappedComponent {...this.props} />;
            }
        }
        HOCComponent.staticMethod = WrappedComponent.staticMethod;
        // ...
        return HOCComponent;
    }
    
    複製代碼
  2. 不要在 render 方法內建立高階組件

  3. 不要改變原始組件(高階組件就是一個沒有反作用的純函數。)

  4. 透傳不相關的 props

解決的問題

  1. 高階組件就是一個沒有反作用的純函數,各個高階組件不會互相依賴耦合
  2. 高階組件也有可能形成衝突,但咱們能夠在遵照約定的狀況下避免這些行爲
  3. 高階組件並不關心數據使用的方式和緣由,而被包裹的組件也不關心數據來自何處。高階組件的增長不會爲原組件增長負擔

存在的問題

  1. HOC 須要在原組件上進行包裹或者嵌套,若是大量使用 HOC,將會產生很是多的嵌套,這讓調試變得很是困難
  2. HOC 能夠劫持 props,存在相同名稱的 props,則存在覆蓋問題在不遵照約定的狀況下也可能形成衝突,並且 react 並不會報錯。
  3. 當存在多個 HOC 時,你不知道 Props 是從哪裏來的。
  4. HOC 屬於靜態構建,靜態構建便是從新生成一個組件,即返回的新組件,不會立刻渲染,即新組件中定義的生命週期函數只有新組件被渲染時纔會執行。

在這個範式下,代碼經過一個相似於 裝飾器(decorator) 的技術進行共享。首先,你的一個組件定義了大量須要被渲染的標記,以後用若干具備你想用共享的行爲的組件包裹它。所以,你如今是在 裝飾 你的組件,而不是混入你須要的行爲!

demos

Render props

Render Props 從名知義,也是一種剝離重複使用的邏輯代碼,提高組件複用性的解決方案。在被複用的組件中,經過一個名爲「render」(屬性名也能夠不是 render,只要值是一個函數便可)的屬性,該屬性是一個函數,這個函數接受一個對象並返回一個子組件,會將這個函數參數中的對象做爲 props 傳入給新生成的組件

它的特色

  1. 傳入函數的屬性,就是  想要共享的 state,這個相同的 state 是組件的狀態,或者行爲
  2. 術語 「render prop」 是指一種技術,用於使用一個值爲函數的 prop 在 React 組件之間的代碼共享。
  3. render prop 僅僅就是一個函數。
  4. render prop是一種模式,重點是 prop,而不是 render,任何被用於告知組件須要渲染什麼內容的函數 prop 在技術上均可以被稱爲 「render prop」.
  5. 這裏的組合模型是 動態的!每次組合都發生在 render 內部,所以,咱們就能利用到 React 生命週期以及天然流動的 props 和 state 帶來的優點。
  6. 具備 render prop 的組件接受一個函數,該函數返回一個 React 元素並調用它,而不是實現本身的渲染邏輯

小栗子

<DataProvider render={data => (
    <h1>Hello {data.target}</h1>
)}/>
複製代碼

優點

  1. 不用擔憂 Props 是從哪裏來的, 它只能從父組件傳遞過來。
  2. 不用擔憂 props 的命名問題。
  3. render props 是動態構建的。

render props demo

能夠用 render props來代替 HOC

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

複製代碼

Hook

在 Hooks 出現之前,咱們老是糾結的問題:

  1. 無狀態組件 VS Class 組件
  2. 生命週期 componentDidMount 和 componentDidUpdate 須要作相同的事情
  3. this 指向 爲了保證 this 的指向正確,咱們常常這麼寫
    this.handleClick = this.handleClick.bind(this)
    <button onClick={() => this.handleClick(e)}>
    複製代碼
  4. HOC 和 render props 增長咱們代碼的層級關係

動機

zh-hans.reactjs.org/docs/hooks-…

Hook 是的做用

  1. 咱們的函數變成了一個有狀態的函數
  2. Hooks 本質上就是一類特殊的函數,它們能夠爲你的函數型組件(function component)注入一些特殊的功能(生命週期鉤子的功能:useEffect;上下文(context):useContext)
  3. 解決 this 指向問題

State Hooks

Effect Hooks

咱們寫的有狀態組件,一般會產生不少的反作用(side effect)。以前都把這些反作用的函數寫在生命週期函數鉤子裏,好比 componentDidMount,componentDidUpdate 和 componentWillUnmount。而如今的 useEffect 就至關與這些聲明周期函數鉤子的集合體。它以一抵三。

用 Effect Hooks 來解決這個這些反作用。

注意點

  1. react 首次渲染和以後的每次渲染都會調用一遍傳給 useEffect 的函數。而以前咱們要用兩個聲明周期函數來分別表示首次渲染(componentDidMount),和以後的更新致使的從新渲染(componentDidUpdate)。
  2. 函數是異步執行的,而以前的 componentDidMount 或 componentDidUpdate 中的代碼則是同步執行的
  3. 怎麼解綁反作用
  4. 跳過一些沒必要要的反作用函數

使用範圍

只能在 React 函數式組件或自定義 Hook 中使用 Hook。

Hook 的提出主要就是爲了解決 class 組件的一系列問題,因此咱們不能在 class 組件中使用它。

相比函數,編寫一個 class 可能須要掌握更多的知識,須要注意的點也越多,好比 this 指向、綁定事件等等。另外,計算機理解一個函數比理解一個 class 更快。Hooks 讓你能夠在 classes 以外使用更多 React 的新特性。

render props hooks

相關文章
相關標籤/搜索