學會使用react高階組件(HOC)優化你的代碼~

React高階組件HOC

😊文章略長,作好內心準備哦,建議閱讀15分鐘。html

定義

  • HOC實際上是一個函數,接收一個組件做爲參數,返回一個包裝組件做爲返回值
  • 在多個不一樣的組件中須要用到相同的功能
  • 高階組件和裝飾器就是一個模式,所以高階組件能夠做爲裝飾器來使用

做用

  • 適用範圍廣,它不須要es6或者其它須要編譯的特性,有函數的地方,就有HOC。
  • Debug友好,它可以被React組件樹顯示,因此能夠很清楚地知道有多少層,每層作了什麼。

補充說明裝飾器

在下面不少案例中用到了裝飾器(@),這裏提早說明下,修飾器(Decorator)是一個函數,用來修改類的行爲react

更多用法可參考簡書一篇文章:www.jianshu.com/p/275bf41f4…es6

基本形式

const EnhancedComponent = higherOrderComponent(WrappedComponent)bash

function hoc(WrappedComponent) {
    return class HOC extends React.Component {
        componentDidMount() {
            console.log("hoc");
        }

        render() {
            return <WrappedComponent />
        }
    }
}

// 使用高階組件
class ComponentClass extends React.Component {
    render() {
        return <div></div>
    }
}

export default hoc(ComponentClass);

// 做爲裝飾器使用
@hoc
export default class ComponentClass extends React.Component {
    //...
}
複製代碼

常見用法

  • 屬性代理(Props Proxy): 高階組件經過ComponentClass的props來進行相關操做
  • 繼承反轉(Inheritance Inversion)):高階組件繼承自ComponentClass

屬性代理(Props Proxy)常見做用

  1. 操做props,能夠對原組件的props進行增刪改查,須要考慮到不能破壞原組件
// 添加新的props
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    render() {
      const newProps = {
        user: currentLoggedInUser
      }
      return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
}
複製代碼
  1. 經過refs訪問組件實例,進而調用組件相關方法
// WrappedComponent初始渲染時候會調用ref回調,傳入組件實例,在proc方法中,就能夠調用組件方法
function refsHOC(WrappedComponent) {
  return class RefsHOC extends React.Component {
    proc(wrappedComponentInstance) {
      wrappedComponentInstance.method()
    }

    render() {
      // Object.asign();將全部可枚舉屬性的值從一個或多個源對象複製到目標對象
      const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})
      return <WrappedComponent {...props}/>
    }
  }
}
複製代碼
  1. 提取state,能夠經過傳入 props 和回調函數把 state 提取出來
// 提取了 input 的 value 和 onChange 方法
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    state = {
        name: ''
    }
    
    onNameChange(event) {
      this.setState({
        name: event.target.value
      })
    }
    
    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onNameChange.bind(this)
        }
      }
       return <WrappedComponent {...this.props} {...newProps}/>
    }
  }
}

// 使用方式以下
@ppHOC
class Example extends React.Component {
  render() {
    // 使用ppHOC裝飾器以後,組件的props被添加了name屬性,input會成爲受控組件
    return <input name="name" {...this.props.name}/>
  }
}
複製代碼
  1. 用其餘元素包裹WrappedComponent,實現佈局,樣式等目的
function ppHOC(WrappedComponent) {
  return class PP extends React.Component {
    render() {
      return (
        <div style={{display: 'block'}}>
          <WrappedComponent {...this.props}/>
        </div>
      )
    }
  }
}
複製代碼

繼承反轉(Inheritance Inversion)常見做用

  1. 渲染劫持,HOC 控制着 WrappedComponent 的渲染輸出,能夠劫持被繼承class的render內容,進行修改,過濾後,返回新的顯示內容
// 過濾掉原組件中的ul元素
function hoc(ComponentClass) {
    return class HOC extends ComponentClass {
        render() {
            const elementTree = super.render();
            elementTree.props.children = elementTree.props.children.filter((z) => z.type !== "ul")
            
            return React.cloneElement(elementTree);
        }
    }
}

@hoc
export default class ComponentClass extends React.Component {
    render() {
        return (
            <div>
                <p style={{color: 'brown'}}>啦啦啦</p>
                <ul>
                    <li>1</li>
                    <li>2</li>
                </ul>
            </div>
        )
    }
}
複製代碼
  1. 操做state,HOC能夠操做WrappedComponent實例的state。但這樣會破壞WrappedComponent的state,因此要限制HOC讀取或添加state,添加的state應該放在單獨的命名空間裏
export function IIHOC(WrappedComponent) {
  return class II extends WrappedComponent {
    render() {
      return (
        <div>
          <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre>
          
          <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre>
          
          {
            super.render()
          }
        </div>
      )
    }
  }
}
複製代碼
  1. 條件渲染
// 假設this.props.loggedIn爲真,HOC會徹底渲染WrappedComponent 的渲染結果
function iiHOC(WrappedComponent) {
  return class ii extends WrappedComponent {
    render() {
      if (this.props.loggedIn) {
        return super.render()
      } else {
        return null
      }
    }
  }
}
複製代碼
  1. 解決WrappedComponent名字丟失問題
// 用HOC包裹的組件會丟失原先的名字,影響開發和調試,能夠在WrappedComponent的名字加前綴來做爲HOC的名字
const componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';

static displayName = `withModal(${componentName})`;
複製代碼

實際應用

  1. 記錄localStorage返回值
//經過多重高階組件肯定key並設定組件
const withStorage = (key) => WrappedComponent => {
  return class extends Component {
    componentWillMount() {
        let data = localStorage.getItem(key);
        this.setState({data});
    }
    render() {
      return <WrappedComponent data={this.state.data} {...this.props} />
    }
  }
}

@withStorage('data')
class MyComponent2 extends Component {  
    render() {
        return <div>{this.props.data}</div>
    }
}

@withStorage('name')
class MyComponent3 extends Component {  
    render() {
        return <div>{this.props.data}</div>
    }
}
複製代碼
  1. 項目中,每次從新打開modal框,每次銷燬modal中數據,防止數據污染
const modalHoc = (options) => WrappedComponent => {
    const componentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';

    return class ModalComponent extends Component {
        static displayName = `withModal(${componentName})`;

        render() {
            const {visible, onCancel} = this.props;

            let title;
            if (typeof options === 'string') title = options;

            if (typeof options === 'function') title = options;

            if (typeof options === 'object') title = options.title;

            if (typeof title === 'function') title = title(this.props);

            return (
                <Modal
                    destroyOnClose
                    width="60%"
                    bodyStyle={{padding: 0}}
                    footer={null}

                    {...options}
                    title={title}

                    onCancel={onCancel}
                    visible={visible}
                >
                    <WrappedComponent {...this.props}/>
                </Modal>
            );
        }
    }
};

@modalHoc('能夠傳入不一樣類型標題')
複製代碼

😊 參考連接:react.html.cn/docs/higher…app

😊 剛剛加入掘金社區,歡迎提出寶貴建議,一塊兒進步學習!函數

相關文章
相關標籤/搜索