<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                &emsp;&emsp;高階組件(High Order Component,簡稱HOC)不是一個真的組件,而是一個沒有副作用的純函數,以組件作為參數,返回一個功能增強的新組件,在很多第三方庫(例如Redux、Relay等)中都有高階組件的身影。由于遵循了裝飾者模式的設計思想,因此不會入侵傳遞進來的原組件,而是對其進行抽象、包裝和拓展,改變原組件的行為(圖9形象的表達出了高階組件的作用)。這樣不僅增強了組件的復用性和靈活性,還保持了組件的易用性。靈活使用高階組件,可大大提高代碼質量。 :-: ![](https://img.kancloud.cn/12/fa/12fa36d385a109a73a727111cdd13b1d_378x378.png =200x) 圖9 高階組件的作用 &emsp;&emsp;高階組件有兩種常見的實現方式:代理和繼承,下面會分別做講解。 ## 一、代理方式 &emsp;&emsp;高階組件作為原組件的代理,不但會將其包裹住,還會給它添加新特性,并且提供了眾多控制原組件的功能,例如操縱props、抽取state、訪問實例和再包裝等。 **1)操縱props** &emsp;&emsp;在原組件(即被包裹組件)接收到props之前,高階組件可以將其攔截,執行增刪改操作,再將處理過的props傳給原組件。下面是一個簡單的示例,會在高階組件中新增了一個name屬性。 ~~~js //原組件 class Btn extends React.Component { render() { return <button>{this.props.name}</button>; } } //高階組件 function HOC(Wrapped) { class Enhanced extends React.Component { constructor(props) { super(props); this.state = { name: "strick" }; } render() { return <Wrapped {...this.state} />; } } return Enhanced; } const EnhancedBtn = HOC(Btn); ~~~ &emsp;&emsp;HOC()函數就是高階組件,在函數體中聲明了用于修飾原組件Wrapped的新組件Enhanced,它的name狀態作為props傳給了Wrapped,并在render()方法中將Wrapped渲染出來。當執行HOC(Btn)后,就能得到增強了的EnhancedBtn組件。 **2)抽取state** &emsp;&emsp;將原組件的state和與之相關的處理函數抽取到高階組件中,從而使得原組件無狀態,變成容易復用的展示型組件。以一個能維護自己狀態的Input組件為例,如下所示。 ~~~js class Input extends React.Component { constructor(props) { super(props); this.state = { value: "" }; this.handle = this.handle.bind(this); } handle(e) { this.setState({ value: e.target.value }); } render() { return ( <input type="text" value={this.state.value} onChange={this.handle} /> ); } } ~~~ &emsp;&emsp;現在將Input組件處理value狀態和onChange事件的代碼提升到高階組件中,如下代碼所示,在render()方法中初始化了一個newProps對象,用于把處理好的value狀態和事件處理程序handle()回傳給Input組件。 ~~~js function stateHOC(Wrapped) { class Enhanced extends React.Component { constructor(props) { super(props); this.state = { value: "" }; this.handle = this.handle.bind(this); } handle(e) { this.setState({ value: e.target.value }); } render() { let newProps = { value: this.state.value, onChange: this.handle }; return <Wrapped {...newProps} />; } } return Enhanced; } ~~~ &emsp;&emsp;經過高階組件的抽象后,Input組件就變得很簡單,如下代碼所示,沒有額外的邏輯操作,只要接收傳過來的props即可。 ~~~js class Input extends React.Component { constructor(props) { super(props); } render() { return <input type="text" {...this.props} />; } } ~~~ **3)Refs** &emsp;&emsp;通過[第5篇](https://www.cnblogs.com/strick/p/10593862.html)提到的Refs訪問方式,可以得到被包裹組件的實例,從而就能操縱組件中的DOM元素。以下面的Btn組件為例,在高階組件中給它定義ref屬性,這樣就能在新組件的componentDidMount()方法中訪問到Btn組件的實例。 ~~~js class Btn extends React.Component { render() { return <button>提交</button>; } } function refHOC(Wrapped) { class Enhanced extends React.Component { render() { return <Wrapped ref={btn => { this.myBtn = btn }} />; } componentDidMount() { console.log(this.myBtn); //Btn組件的實例 } } return Enhanced; } ~~~ &emsp;&emsp;注意,ref屬性不會傳遞給原組件,如果在上面的Btn組件中讀取this.props.ref,那么得到的值將是undefined,如下所示。 ~~~js class Btn extends React.Component { render() { console.log(this.props.ref); //undefined } } ~~~ **4)包裝** &emsp;&emsp;在高階組件中,還能通過引入其它React元素包裝原組件,既能改變布局,也能增加樣式。例如用一個包含內邊距的元素包裹原組件,并在其鄰近的位置新增一個文本框,如下所示。 ~~~js function wrappedHOC(Wrapped) { class Enhanced extends React.Component { render() { return ( <div style={{ padding: 10 }}> <input type="text" /> <Wrapped /> </div> ); } } return Enhanced; } ~~~ ## 二、繼承方式 &emsp;&emsp;繼承是另一種構建高階組件的方式,即新組件直接繼承原組件(如下代碼所示),從而實現通用邏輯的復用,并且還能使用原組件的state和props,以及生命周期等方法。 ~~~js function inheritHOC(Wrapped) { class Enhanced extends Wrapped { } return Enhanced; } ~~~ &emsp;&emsp;在代理方式下的新組件和原組件會各自經歷一次完整的生命周期,而在繼承方式下,兩者會共用一次生命周期。 **1)渲染劫持** &emsp;&emsp;在高階組件中,可以通過super.render()渲染原組件,從而就能控制高階組件的渲染結果,即渲染劫持。例如在新組件的render()方法中克隆原組件并為其傳遞新的props,如下所示。 ~~~js function inheritHOC(Wrapped) { class Enhanced extends Wrapped { render() { //獲取原組件 const origin = super.render(); //合并原組件的屬性,并新增value屬性的值 const props = Object.assign({}, origin.props, {value: "strick"}); return React.cloneElement(origin, props, origin.props.children); } } return Enhanced; } ~~~ &emsp;&emsp;代碼中的React.cloneElement()方法能接收3個參數,第一個是要克隆的React元素,后兩個是要傳遞的新props和原來的children屬性。 &emsp;&emsp;除了render()方法,其余諸如componentWillMount()、componentWillUpdate()等生命周期中的方法也是能劫持的。 **2)使用state** &emsp;&emsp;在高階組件中,不僅可以讀取原組件的state,還能對其進行修改或增加,甚至是刪除。不過,這三類帶有侵略性的操作,會讓原組件內部變得混亂不堪,因此要慎用。在下面的示例中,Input組件包含一個value狀態,高階組件內的新組件Enhanced會在其構造函數中增加一個name狀態,并修改value狀態的值。 ~~~js class Input extends React.Component { constructor(props) { super(props); this.state = { value: "" }; } render() { return <input type="text" value={this.state.value} />; } } function stateHOC(Wrapped) { class Enhanced extends Wrapped { constructor(props) { super(props); this.state.name = "strick"; //增加狀態 this.state.value = "init"; //修改狀態 } render() { return super.render(); } } return Enhanced; } let EnhancedInput = stateHOC(Input); ~~~ ## 三、參數傳遞 &emsp;&emsp;高階組件除了一個組件參數之外,還能接收其它類型的參數,例如為高階組件額外傳遞一個區分類別的type參數,如下所示。 ~~~js HOC(Wrapped, type) ~~~ &emsp;&emsp;不過,在React中,函數式編程的參數傳遞更為常用,即使用柯里化的形式,如下代碼所示,其中HOC(type)會返回一個高階組件。 ~~~js HOC(type)(Wrapped) ~~~ &emsp;&emsp;而在第三方庫中,這種形式的高階組件被大量應用,例如Redux中用于連接React組件與其Store的connect()函數,它是一個能返回高階組件的高階函數,其參數可以是兩個函數,如下所示。 ~~~js const Enhanced = connect(mapStateToProps, mapDispatchToProps)(Wrapped); ~~~ &emsp;&emsp;將上面這條語句拆分成兩條目的更為清晰的語句,就能讓人更容易理解代碼的意圖,如下所示。 ~~~js const enhance = connect(mapStateToProps, mapDispatchToProps); const Enhanced = enhance(Wrapped); ~~~ &emsp;&emsp;雖然這種形式的高階組件會讓人困惑,但是更易于組合。因為它會把參數序列處理到只剩一個組件參數,而高階組件的返回值也是一個組件,也就是說,前一個高階組件的返回值可以作為后一個高階組件的參數,從而使得這些高階組件可以組合在一起。例如有三個高階組件f、g和h,它們可以像下面這樣組合在一起。 ~~~js f(g(h(Wrapped))) ~~~ &emsp;&emsp;如果要嵌套的高階組件很多,那么這種寫法將變得異常丑陋且難以閱讀。這個時候,就可以引入compose()函數,它能將函數串聯起來,即用平鋪的寫法實現函數的組合,如下代碼所示,省略了compose()函數的具體實現。 ~~~js compose(f, g, h) ~~~ &emsp;&emsp;compose()函數的執行方向是自右向左,并且還有一個限制,那就是第一個高階組件(即h)可以接收多個參數,但之后的就只能接收一個參數。 ## 四、命名 &emsp;&emsp;在高階組件中創建的新組件,不會再沿用原組件的名稱。為了便于在React Developer Tools中調試,需要為新組件設置一個顯示名稱,例如新組件的名稱是“Enhanced”,原組件的名稱是“Input”,那么就以“Enhanced(Input)”為顯示名稱。 &emsp;&emsp;為了完成這個功能,高階組件可以修改成下面這樣。注意,在定義displayName屬性時,用到了ES6新增的模板字面量。 ~~~js function HOC(Wrapped) { class Enhanced extends React.Component { } Enhanced.displayName = `Enhanced(${getDisplayName(Wrapped)})`; return Enhanced; } ~~~ &emsp;&emsp;getDisplayName()函數用于獲取組件的名稱(如下代碼所示),如果組件的displayName屬性不存在,那么就改用name屬性,即類或函數的名稱。 ~~~js function getDisplayName(Wrapped) { return Wrapped.displayName || Wrapped.name || "Component"; } ~~~ ## 五、注意事項 &emsp;&emsp;(1)不要在組件的render()方法中使用高階組件。因為高階組件每次都會創建一個新組件,而根據React的diff算法可知,原組件(即前一次所創建的組件)會先被卸載掉,然后重新掛載新組件。這么做不僅性能低下,而且原組件的狀態和其所有子組件都將丟失。 &emsp;&emsp;(2)高階組件創建的新組件不會包含原組件的靜態方法,如果需要,那么就得手動復制。 ***** > 原文出處: [博客園-React躬行記](https://www.cnblogs.com/strick/category/1455720.html) [知乎專欄-React躬行記](https://zhuanlan.zhihu.com/pwreact) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看