React Forwarding高階組件傳遞Refs

在通常組件中使用Forwarding Refs

一般狀況下,咱們想獲取一個組建或則一個HTML元素的實例經過 Ref特性 就能夠實現,可是某些時候咱們須要在子父級組建中傳遞使用實例,Forwarding Refs提供了一種技術手段來知足這個要求,特別是開發一些重複使用的組建庫時。好比下面的例子:javascript

function MyButton(props) {
  return (
    <button className="MyButton">
      {props.children}
    </button>
  );
}

上面的代碼中MyButton組件渲染了一個HTML元素。對於使用者而言,React隱藏了將代碼渲染成頁面元素的過程,當其餘組件使用MyButton時,並無任何直接的方法來獲取MyButton中的<button>元素,這樣的設計方法有利於組建的分片管理,下降耦合。java

可是像MyButton這樣的組建,其實僅僅是對基本的HTML元素進行了簡單的封裝。某些時候,上層組建使用他時更但願將其做爲一個基本的HTML元素來看待,實現某些效果須要直接操做DOM,好比focus、selection和animations效果。react

下面的例子將Forwarding Refs添加到MyButton組件中,以實現實例傳遞的效果。app

const MyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="MyButton">
    {props.children}
  </button>
));

// 經過ref能夠直接操做<button>元素:
const ref = React.createRef();
<MyButton ref={ref}>Click me!</MyButton>;

這個時候,ref能夠直接操做<button>元素。其實執行過程很是簡單,也就下面5步:dom

  1. 經過React.createRef()方法建立一個ref實例。
  2. 和一般使用Ref同樣,將其做爲一個ref屬性參數傳遞給MyButton組件。
  3. 使用React.forwardRef方法來建立一個組件,並將ref做爲第二個參數傳遞。
  4. 將ref參數以ref屬性的方式傳遞給<button>元素。
  5. 在渲染以後,可使用ref.current來獲取<button>元素的實例。

須要注意的是隻有使用React.forwardRef來建立一個組件時,第二個ref參數纔會存在。固定的方法或者使用類來建立組件並不會接收到ref參數。Forwarding Refs特性並不單單侷限於用在HTML DOM元素上,這種方式也實用於組件之間傳遞Ref。 工具

在高階組件中使用Forwarding Refs

高階組件(HOCs)僅僅對通常組件的包裝。通常組件被包裝以後對於使用者來講並不清晰其是不是被包裝過,此時使用Ref獲得的是高階組件的實例。所以Forwarding Refs特性對於高階組件來講更有價值。ui

下面是一個高階組件記錄日誌的例子:this

function logProps(WrappedComponent) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  }

  return LogProps;
}

logProps組件用於在每次數據更新先後記錄props中的數據。咱們用其包裝前面的MyButton組件。spa

class MyButton extends React.Component {
  focus() {
    // ...
  }

  render() {
    //
  }
}

export default logProps(MyButton);

此時經過import並使用Refs實際上獲得的是LogProps的實例:設計

import FancyButton from './FancyButton';

const ref = React.createRef();
<MyButton
  label="Click Me"
  handleClick={handleClick}
  ref={ref}
/>;

咱們使用Forwarding Refs對高階組件進行簡單的改造便可解決這個問題:

function logProps(Component) {
  class LogProps extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('old props:', prevProps);
      console.log('new props:', this.props);
    }

    render() {
      const {forwardedRef, ...rest} = this.props;

      // 經過forwardedRef參數傳遞ref的值
      return <Component ref={forwardedRef} {...rest} />;
    }
  }
  
  //而後使用 React.forwardRef 來包裝建立 LogProps組件的實例
  //注意這裏使用 forwardedRef 來傳遞 父組件的 ref
  //
  return React.forwardRef((props, ref) => {
    return <LogProps {...props} forwardedRef={ref} />;
  });
}

開發調試組件名稱顯示

若是咱們不進行任何調整,下面的代碼在調試工具中輸出的組件名稱爲:"ForwardRef(MyComonent)":

const WrappedComponent = React.forwardRef(
  function myFunction(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }
);

能夠經過displayName來設定想要現實的名字:

function logProps(Component) {
  class LogProps extends React.Component {
    // ...
  }

  //先定義返回的高階組件方法
  function forwardRef(props, ref) {
    return <LogProps {...props} forwardedRef={ref} />;
  }

  //而後設定這個組件的名稱
  const name = Component.displayName || Component.name;
  forwardRef.displayName = `logProps(${name})`;

  //構建組件
  return React.forwardRef(forwardRef);
}
相關文章
相關標籤/搜索