七進七出React高階元件

尼古拉斯李三發表於2017-11-01

為什麼要使用高階元件?

想想以前用原生和jQuery的專案,上千行的code映入眼簾,瞬間有種昏死過去的衝動。程式碼難以維護,改一個bug可能出現N個bug,真的是很痛苦。於是乎元件化成為了當前前端開發的主流技術。angular、vue和react很好的幫我們實現了元件化。

但是我們常常也會遇到一種情況,就是兩個元件往往有很多的重複程式碼(可能是相同的屬性,也可能是相同的方法)。例如,在登入和註冊元件中,都會有使用者名稱和密碼,以及對他們的校驗規則。為了提高程式碼的複用性和可維護性,React高階函式應運而生。

React之前對此的解決方案是mixin,但這造成和很多不必要的問題,後來就被廢棄掉了。使用過vue的同學,不知道有沒有使用過mixin,react高階函式的作用和它是一樣的。

高階元件到底是個什麼東西?

高階元件其實是一個函式,它並不是一個元件,我們需要向它傳遞一些引數(想要操作的元件、屬性)。這麼說起來它其實一點也不高階,它的作用就是儲存一些公共的屬性和方法。

我們經常幾個人吃過一個鍋底的那種火鍋,鍋底裡有火鍋底料和各種佐料,我們把肉和蔬菜等放進去涮一下,就可以美美的飽餐一頓。高階函式在react程式設計中扮演的角色就是火鍋鍋底的角色,它有公用的方法和屬性,而各種元件就是肉和蔬菜。如果是一個人一個鍋的火鍋就像沒有經過封裝的code,程式碼量重複且維護困難。

如何實現高階函式?

我們先來看一段可以使用高階函式的程式碼:

這裡有一個Second元件,它負責展示使用者名稱username

export class Second extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }
    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        });
    }
    render() {
        return(
            <div>
                <legend>Second Page</legend>
                <h2>Hi {this.state.username}</h2>
            </div>
        )
    }
}複製程式碼

下面是一個Third元件,它也負責展示使用者名稱

export class Third extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            username: ''
        }
    }
    componentWillMount() {
        let username = localStorage.getItem('username');
        this.setState({
            username: username
        });
    }
    render() {
        return(
            <div>
                <legend>Third Page</legend>
                <h2>Hi {this.state.username}</h2>
                <p>我今年18歲了</p>
            </div>
        )
    }
}複製程式碼

我們看到這兩個元件除了名稱以外,其餘大量的程式碼是一樣的,我們完全可以考慮將它們通用的程式碼提取出來。下面就是高階元件出場的時候了。

高階元件可以寫成這樣

export const HighOrderComponent = (WrapComponent, title) => {
    return class HOC extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                username: ''
            }
        }
        componentWillMount() {
            let username = localStorage.getItem('username');
            this.setState({
                username: username
            });
        }
        static displayName = `HOC(${getDisplayName(WrapComponent)})`;
        render() {
            return(
                <div>
                    <legend>{title}</legend>
                    <WrapComponent username={this.state.username}></WrapComponent>
                </div>
            )
        }
    }
}複製程式碼
  • WrapComponent 是我們要操作的元件
  • title 是它們的標題引數
  • username 就是我們常用的props,它負責向元件傳遞值,我們下面還會提到

使用高階元件之後我們要對Second和Third元件進行修改,修改如下

class Second extends React.Component {
    render() {
        return(
            <div>
                <h2>Hi {this.props.username}</h2>
                <h3>曉不曉得哪裡好耍</h3>
            </div>
        )
    }
}
export const HighOrderSecond = HighOrderComponent(Second, 'Second Page');複製程式碼
class Third extends React.Component {

    render() {
        return(
            <div>
                <h2>Hi {this.props.username}</h2>
            </div>
        )
    }
}
export const HighOrderThird = HighOrderComponent(Third, 'Third Page');複製程式碼

很多同學可能會問,export出去的常量是幹什麼用的,它就是高階元件對元件進行封裝之後的一個全新的元件,是兩者的結合。到此,我們在其它頁面引用元件就不再是引用Second和Third元件了,要引用的就是HighOrderSecond和HighOrderThird了。

細心的同學可能會發現一些不同的地方,對於Second和Third的公共legend提取出去了,但並沒有將h2對應得標題提取出去,這裡只是想給大家說一下,在對元件使用高階元件包裹之後,高階元件就變成了元件的父元件,它的state可以通過props的方式向子元件傳遞,username就是這樣。我們在chrome的react外掛中可以觀察到這一點


HOC已經將Second元件包裹起來,成為了它的父元件,它上面的match和location是我使用了react-router的緣故,我們可以不用去管它。它的state——username作為props傳給了Second


今天給大家簡單的介紹了React高階元件的一些知識,後續還會再深入的挖掘React高階元件的知識和大家分享,希望對大家有幫助。

相關文章