動手寫個React高階組件

是什麼

簡稱HOC,全稱 High Order Component。做用是給react組件增減props屬性。react

怎麼用

爲何不先說怎麼寫?恩,由於你其實已經用過了,舉個例子:redux

// App.js

import {connect} from 'react-redux';

class App extends React.Component {
  render() {}
}

export default connect()(App);

熟悉不?redux的鏈接器。不過筆者有潔癖,喜歡用裝飾器:瀏覽器

// App.js

import {connect} from 'react-redux';

@connect()
export class App extends React.Component {
  render() {}
}

開始寫

connect()()能夠看出,connect是一個函數,返回值是個react組件。這麼聰明,好佩服本身啊。app

雛形

// myHoc.js
import React from 'react';

export const myHoc = () => {
  return (Wrapped) => {
    class Hoc extends React.Component {
      render() {
        return <Wrapped {...this.props}>;
      }
    }
    
    return Hoc;
  };
};

是的,高階組件的雛形,就是函數裏隱藏了一個react組件,而參數Wrapped是什麼?就是下面被裝飾的組件:函數

// App.js

@myHoc()
export class App extends React.Component {
  render() {}
}

恩恩,表現形式和redux的connect如出一轍。
因此用了高階組件後,export出去的再也不是你本身寫的App(Class),而是最後一個高階。this

增長props屬性

好的啦,如今用myHoc給App組件加點料:code

// myHoc.js
export const myHoc = () => {
  return (Wrapped) => {
    class Hoc extends React.Component {
      render() {
        return <Wrapped {...this.props} whoAmI="原罪">;
      }
    }
    
    return Hoc;
  };
};
// App.js

@myHoc()
export class App extends React.Component {
  render() {
    return <div>{this.props.whoAmI}</div>;
  }
}

放心,此刻瀏覽器裏已經把個人名字 原罪 打印出來了。component

多個高階組件

是的,寫完一個hoc以後,你就會有寫第二個的需求,那就一塊兒用呢:console

// App.js

@myHoc()
@yourHoc()
@hisHoc()
@herHoc()
export class App extends React.Component {
  render() {
    return <div>{this.props.whoAmI}</div>;
  }
}

這就是筆者爲啥要用裝飾器的緣由,簡潔,看起來舒服,寫起來快,咱們看一下另外一種寫法:class

class App extends React.Component {
  render() {}
}

export default myHoc()(yourHoc()(hisHoc()(herHoc()(App))));

本身體會,格式化一下吧:

class App extends React.Component {
  render() {}
}

let hoc;
hoc = herHoc()(App);
hoc = hisHoc()(hoc);
hoc = yourHoc()(hoc);
hoc = myHoc()(hoc);

export default hoc;

寫得累不?來,給你條毛巾擦擦汗

帶參數

對了,hoc能夠接收參數,好比這樣:

// App.js

@myHoc('原罪2號')
export class App extends React.Component {
  render() {
    return <div>{this.props.whoAmI}</div>;
  }
}

那高階組件怎麼接呢?

// myHoc.js

export const myHoc = (name) => {
  return (Wrapped) => {
    class Hoc extends React.Component {
      render() {
        return <Wrapped {...this.props} whoAmI={name}>;
      }
    }
    
    return Hoc;
  };
};

我把hoc接收到的參數又返還給了App組件,那如今瀏覽器輸出的就是:原罪2號

不帶參數

如今,你可能有一個大膽的插法..哦不,想法,就是@myHoc後面能夠不加括號嗎?是哦,看前面幾個案例,都是@myHoc()。好的,看個人:

// myHoc.js

export const myHoc = (Wrapped) => {
  class Hoc extends React.Component {
    render() {
      return <Wrapped {...this.props} whoAmI="原罪">;
    }
  }
    
  return Hoc;
};
// App.js

@myHoc
export class App extends React.Component {
  render() {
    return <div>{this.props.whoAmI}</div>;
  }
}

細心的看官看一下myHoc.js和帶參數的時候有什麼區別。是的,少了一層回調。若是你的高階組件不須要帶參數,這樣寫也是很ok的。

操控原組件

你可能須要拿被裝飾的組件的state數據或者執行它的方法。那麼須要創建一個引用:

// myHoc.js

import React from 'react';

export const myHoc = () => {
  return (Wrapped) => {
    class Hoc extends React.Component {
      appRef = null;
      
      componentDidMount() {
        // 能夠對被myHoc裝飾的組件作羞羞的事情了,:)
        console.log(this.appRef);
      }
    
      render() {
        return <Wrapped {...this.props} ref={(app) => {this.appRef = app}} >;
      }
    }
    
    return Hoc;
  };
};

注意: 在多個高階組件裝飾同一個組件的狀況下,此法並不奏效。你拿到的ref是上一個高階組件的函數中臨時生成的組件。並且在大多數狀況下,你並不知道某個組件會被多少個高階裝飾!

總結

當項目中多處用到某個邏輯方法,可是這個邏輯不能放到util裏的時候,HOC適合你。一個HOC最好只作一件事,這樣維護方便

相關文章
相關標籤/搜索