React我的入門總結《三》

簡介

此次總結的是 高階組件context ,這些都是React的核心知識,學會了不只對 設計模式 有必定的接觸,還會對 架構思想 更加的理解。react

context 瞭解以後對以後的 Redux 學起來也沒那麼費勁,而 HOC 會讓你書寫 React 中的代碼更加優雅,抽出重複的邏輯避免過多的重複代碼。es6

高階組件(HOC)

在多個不一樣的組件中須要用到相同的功能,一般有 Mixin 和 高階組件 ,可是因爲過多的 Mixin 會致使項目維護起來苦難,因此 高階組件 替代了 Mixinredux

高階組件實際上是一個函數,接收一個組件做爲參數,返回一個新的包裝組件做爲返回值,相似於高階函數。高階組件和裝飾器就是一個模式,所以,高階組件能夠做爲 裝飾器 來使用,高階組件 有以下好處:設計模式

  • 適用範圍廣,它不須要es6或者其它須要編譯的特性,有函數的地方,就有 高階組件(HOC) 。
  • Debug友好,它可以被React組件樹顯示,因此能夠很清楚地知道有多少層,每層作了什麼。
<!-- renderContent -->
import React, { Component } from 'react';
import wrapWithContent from './wrapWithContent'; // 引入高階組件

class RenderContent extends Component {
    render() {
        console.log(this.props)
        return(
            <div className="header">
                <!-- 輸出獲取到的參數 -->
                <h1 style={{backgroundColor: 'red'}}>{this.props.message}</h1>
            </div>
        )
    }
}

RenderContent = wrapWithContent(RenderContent) // 調用時把一個組件傳過去
export default RenderContent


<!-- wrapWithContent -->
import React, { Component } from 'react';

<!-- 聲明一個函數並返回一個新組件 -->
const hoc = function(ComponentClass) {
    return class HOC extends Component {
        constructor(props) {
            super(props);
            this.state = {
                message: '高階組件'
            }
        }

        render() {
            <!-- 傳遞參數 -->
            return <ComponentClass message={this.state.message} />
        }
    }
}

export default hoc
複製代碼

這樣就能夠在別的組件引用而且使用它,複用性極強。bash

代碼複用的方法、形式有不少種,你能夠用類繼承來作到代碼複用,也能夠分離模塊的方式,它其實就是設計裏面的 裝飾者 模式:架構

import React, { Component } from 'react';
import wrapWithContent from './wrapWithContent';

@wrapWithContent // 使用 decorator(裝飾器)
class RenderContent extends Component {
    
    constructor(props) {
        super(props)
        this.state = {
            renderContentState: true
        }
    }

    render() {
        console.log(this.props)
        return(
            <div className="header">
                <h1>{this.props.message}</h1>
            </div>
        )
    }
}
複製代碼

下面是高階組件經常使用的方式:app

  • 屬性代理(Props Proxy): 高階組件經過 ComponentClassprops 來進行相關操做
    1. 操做 props
      <!-- 能夠對原組件的props進行增刪改查,一般是查找和增長,刪除和修改的話,須要考慮到不能破壞原組件 -->
       
       <!-- HOC -->
       const hoc = function(ComponentClass) {
           return class HOC extends Component {
               constructor(props) {
                   super(props);
                   this.state = {
                       message: '我要在高階組件傳遞信息'
                   }
               }
       
               render() {
                   <!-- 這裏能夠獲取原生組件的 props -->
                   console.log(this.props, '老子是高階組件')
                   <!-- 這裏添加 props -->
                   return <ComponentClass message={this.state.message} />
               }
           }
       }
       
      複製代碼
    2. 經過 refs 訪問組件實例。
      <!-- 能夠經過ref回調函數的形式來訪問傳入組件的實例,進而調用組件相關方法或其餘操做 -->
      
      <!-- HOC -->
      const hoc = function (ComponentClass) {
          return class HOC extends Component {
              constructor(props) {
                  super(props);
                  this.state = {
                      message: '我要在高階組件傳遞信息'
                  }
              }
      
              getRenderContentInstance(wrappedComponentInstance) {
                  <!-- 打印原組件的實例 -->
                  console.log(wrappedComponentInstance)
              }
      
              render() {
                  const props = {
                      ref: this.getRenderContentInstance.bind(this)
                  }
                  
                  <!-- 這裏傳遞時會當即調用 ref ,而且得到原組件的實例 -->
                  return <ComponentClass {...props} />
              }
          }
      }
      複製代碼
    3. 提取 state
      <!-- 你能夠經過傳入 props 和回調函數把 state 提取出來。 -->
      <!-- 把 input 變成受控組件,而且把狀態抽出來 -->
      
      <!-- HOC -->
       const hoc = function (ComponentClass) {
           return class HOC extends Component {
               constructor(props) {
                   super(props);
                   this.state = {
                       name: ''
                   }
               }
               
               onChangeUserName(event) {
                   this.setState({
                       name: event.target.value
                   })
               }
       
               render() {
                   const attrbutes = {
                       value: this.state.name,
                       onChange: this.onChangeUserName.bind(this)
                   }
                   return <ComponentClass attrbutes={attrbutes} />
               }
           }
       }
       
       <!-- renderContent -->
       class RenderContent extends Component {
           render() {
               console.log(this.props)
               return(
                   <div className="header">
                       <input type="text" {...this.props.attrbutes} />
                   </div>
               )
           }
       }
      複製代碼
    4. 用其餘元素包裹 WrappedComponent ,實現佈局等目的。
      <!-- 爲了封裝樣式、佈局等目的,能夠將WrappedComponent用組件或元素包裹起來,這樣組件裏面的內容都會有邊框包起來。 -->
      
      <!-- HOC -->
      const hoc = function (ComponentClass) {
          return class HOC extends Component {
              constructor(props) {
                  super(props);
                  this.state = {
                      message: '老子是高階組件'
                  }
              }
      
              render() {
                  return (
                      <div style={{border: '2px solid green'}}>
                          <ComponentClass message={this.state.message} />
                      </div>
                  )
              }
          }
      }
      複製代碼

總結來講 高階組件就是一個函數,傳給它一個組件,它返回一個新的組件。新的組件使用傳入的組件做爲子組件。函數

高階組件 的做用是用於代碼複用,能夠把組件之間可複用的代碼、邏輯抽離到高階組件當中。新的組件和傳入的組件經過 props 傳遞信息。佈局

context

因爲第二節總結說的 狀態提高 在多層組件嵌套之下,要維護起來很是困難,這時若是有個全局狀態全部組件都能直接獲取的話,就方便多了。post

context 其實像就是組件樹上某顆子樹的全局變量。

某個組件只要往本身的 context 裏面放了某些狀態,這個組件之下的全部子組件都直接訪問這個狀態而不須要經過中間組件的傳遞。一個組件的 context 只有它的子組件可以訪問,它的父組件是不能訪問到的。

import PropTypes from 'prop-types'

<!-- index -->
class Index extends Component {
    <!-- 驗證 getChildContext 返回的對象  -->
    static childContextTypes = {
        globalData: PropTypes.object
    }

    constructor(props) {
        super(props)
        this.state = {
            globalData: {
                name: 'jack',
                age: 18
            }
        }
    }
    <!-- 設置 context -->
    getChildContext() {
        return {
            globalData: this.state.globalData
        }
    }

    render() {
        return (
            <div className="index">
                <Child />
            </div>
        )
    }
}

<!-- child -->
class Child extends Component {
    <!-- 子組件驗證 context -->
    static contextTypes = {
        globalData: PropTypes.object 
    }

    render() {
        console.log(this.context)
        return (
            <div className="child"></div>
        )
    }
}
複製代碼

不管是父組件設置 context , 仍是子組件獲取 context 都須要進行數據驗證,這說明 context 這玩意不能濫用,可是這個特性對於 redux 很重要。

父組件經過 getChildContext 設置 context ,用 childContextTypes 驗證 getChildContext 返回的數據,子組件用 contextTypes 驗證父級設置 context 的數據,而後就能夠獲取到 context

context 打破了組件和組件之間經過 props 傳遞數據的規範,極大地加強了組件之間的耦合性。並且,就如全局變量同樣,context 裏面的數據能被隨意接觸就能被隨意修改,每一個組件都可以改 context 裏面的內容會致使程序的運行不可預料。


上一篇 --- React我的入門總結《二》

下一篇 --- React我的入門總結《四》

相關文章
相關標籤/搜索