[React翻譯]Refs and the DOM

通常狀況在一個正常的dataflow中,父組件老是將data經過props傳給子組件,而後子組件re-render。可是有的時候咱們想當即修改一個react子組件或一個DOM,那該怎麼辦呢?node

對的,就是用refsreact

refs提供了一個DOM節點或react組件實例的引用,這樣咱們在組件中就能訪問到那個DOM節點或react組件實例dom

何時用refs

  • 處理focus,text 選擇,或者視頻回放
  • 觸發交互式動畫
  • 整合第三方DOM庫

不要使用ref去作能夠聲名的事情。比方說不要讓Dialog組件暴露open()和close()方法,而是使用props.isOpen去管理Dialog函數

建立refs

用React.createRef()建立refs的,而後經過ref屬附加給一個React元素或者DOM。在構造組件時,一般將Refs分配給實例屬性,以即可以在整個組件中引用它們。動畫

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />; } } 複製代碼

訪問refs

在生命週期方法或者事件處理器中可使用 current 屬性訪問refthis

const node = this.myRef.current
複製代碼
  • 若是 ref 是給HTML元素用的,那麼this.myRef.current 就是一個DOM
  • 若是ref 是給react組件用的,那麼this.myRef.current 就是當前組件的實例
  • ref 不能用在函數式組件中,由於函數式組件沒有實例

給DOM加ref

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // 1. 首先建立ref
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }

  focusTextInput() {
    // 3. 最後,咱們在事件處理器中就能使用原生的DOM方法了
    this.textInput.current.focus();
  }

  render() {
    // 2. 而後,告訴react咱們但願把構造器裏定義的textInput 指向 <input>
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
複製代碼
  1. 組件mount的時候current會被賦值爲DOM,固然了還沒mount的時候是null。
  2. ref會在componentDidMount or componentDidUpdate 以前更新。

給class組件加ref

比方說咱們想要一個AutoFocusTextInput組件,這個組件就是在mount以後調一下CustomTextInput的focusTextInput()方法:spa

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }

  componentDidMount() {
    this.textInput.current.focusTextInput();
  }

  render() {
    return (
      <CustomTextInput ref={this.textInput} /> ); } } 複製代碼

注意CustomTextInput 必須是class型組件code

給function組件加ref

render的時候不能給一個function組件加refcomponent

function MyFunctionComponent() {
  return <input />;
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // This will *not* work!
    return (
      <MyFunctionComponent ref={this.textInput} />
    );
  }
}
複製代碼

可是一個function組件內部仍是能用ref的,只要這個ref不是attach到function組件的視頻

function CustomTextInput(props) {
  // textInput must be declared here so the ref can refer to it
  let textInput = React.createRef();

  function handleClick() {
    textInput.current.focus();
  }

  return (
    <div>
      <input
        type="text"
        ref={textInput} />
      <input
        type="button"
        value="Focus the text input"
        onClick={handleClick}
      />
    </div>
  );
}
複製代碼

暴露DOM refs給父組件

想不想在父組件裏面訪問一個子DOM節點呢?

不,你不想。

由於這破壞了組件的封裝。

但有些時候仍是能夠用的,好比說觸發一個focus或者測量DOM節點的大小和屬性。

上面給class組件加ref的例子,有的時候也不是最理想的,由於這個時候返給咱們的是一個react組件,而不是一個DOM。

forward refs能讓組件選擇暴露哪些子組件的refs供本身用。

建議不要暴露DOM節點,但它能夠是一個escape hatch。這個方法要向子組件添加一些代碼。而後若是咱們徹底沒法控制子組件實現,最後一個選項是使用findDOMNode(),但在StrictMode中是不能用的。

回調refs

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };

    this.focusTextInput = () => {
      // Focus the text input using the raw DOM API
      if (this.textInput) this.textInput.focus();
    };
  }

  componentDidMount() {
    // autofocus the input on mount
    this.focusTextInput();
  }

  render() {
    // Use the `ref` callback to store a reference to the text input DOM
    // element in an instance field (for example, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}
複製代碼

ref不只可使用React.createRef()建立,還可使用一種叫回調ref的方法實現。咱們給render函數裏的dom加上一個回調ref,如上面的this.setTextInputRef。在掛載組件的時候react會調用回調ref,而後這個回調的參數會被react認爲是當前的dom。這上面的例子中,回調ref,也就是this.setTextInputRef將這個dom的引用賦給了this.textInput。

組件之間能夠傳回調refs。

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  render() {
    return (
      <CustomTextInput
        inputRef={el => this.inputElement = el}
      />
    );
  }
}
複製代碼

Parent給 inputRef prop 賦了一個回調,而後傳給CustomTextInput,完了CustomTextInput又把這個回調給了input當作一個回調refs。這樣的結果是啥呢?是Parent裏頭的this.inputElement會賦值爲一個DOM,而這個DOM偏偏就是CustomTextInput裏頭的input

有回調refs的注意事項

ref 回調定義爲內聯函數的話更新的時候會被調用2次,1次refs是null,1次是DOM。這是由於每次渲染都會建立一個新的函數實例,因此react須要先清除舊的ref而後再設置新的ref。

能夠把ref callback給bind到class上,不過大多數狀況下這個事影響不大

相關文章
相關標籤/搜索