React.createRef()


概述:

    引用(Refs)提供了一個獲得DOM節點或者創建在render方法中的React元素的方法;

    在典型的React數據流中,props是唯一的父組件與它們的子元素的通信方式。更改子元素,你需要使用新的props去重新渲染子元素。但是在一些情況下你現在典型數據流之外強制的更改元素。被更改的子元素可能是一個React組件的實例,或者是一個DOM元素。對所有這些情況,React提供了一種特殊方法:Refs;

(一)什么時候使用Refs:

  • 管理焦點、文本選擇、媒體回放
  • 觸發必要動畫;
  • 整合第三方DOM庫

    避免對任何可以聲明式解決的問題使用Refs;(比如相對於暴露一個對話框組件的open()、close()方法,請使用isOpen prop!)

(二)不要過度使用Refs!

    你的第一個傾向可能是使用Refs去實現一些APP中的東西。在這種情況下,請停下來,仔細想想state應該存在的組件層次。經常地,我們都知道應該由更高的層次去擁有state。

(三)創建Refs:

    可以通過React.createRef()創建Refs並通過ref屬性聯系到React組件。Refs通常當組件被創建時被分配給實例變量,這樣它們就能在組件中被引用。

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

(四)訪問Refs:

    當一個ref通過render放入一個元素中,一個對節點的引用可以通過ref的current屬性得到;

const node = this.myRef.current;

    ref的值根據節點類型的不同而不同:

  •     當ref屬性用於HTML元素,在構造器中通過React.createRef()函數創建的ref接收底層DOM元素作為它的current屬性;
  •     當ref屬性用於傳統的類組件,ref對象接收掛載好的組件實例作為它的current;
  •     你不能將ref屬性用於函數式組件上,因為他們並沒有實例(instance)!

    下面是對應於不同ref的例子

1)DOM元素:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    // 創建一個ref去儲存textInput DOM元素
    this.textInput = React.createRef();
    this.focusTextInput = this.focusTextInput.bind(this);
  }
 
  focusTextInput() {
    // 很明顯的,讓text input獲得焦點使用了原生的DOM API
    // 注意:我們通過current去獲得DOM節點
    this.textInput.current.focus();
  }
 
  render() {
    // 告訴React我們想要將<input>的ref和構造器中創建的textInput聯系起來
    return (
      <div>
        <input
          type="text"
          ref={this.textInput} />
 
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

   React將會將會在組件掛載時將DOM元素分配給current屬性,並且在組件被卸載時,將current屬性重置為null。ref將會在componentDidMount和componentDidUpdate生命周期鈎子前被更新

2)類組件:

    如果我們想要包裝上面的CustomTextInput,模仿掛載后被點擊。我們可以通過ref得到自定義的Input組件,手動調用它的focusTextInput函數。(注意!只有當CustomTextInput被聲明為類的時候才有用!)

class AutoFocusTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
 
  componentDidMount() {
    this.textInput.current.focusTextInput();
  }
 
  render() {
    return (
      <CustomTextInput ref={this.textInput} />
    );
  }
}

3)函數式組件(沒有用的!)

function MyFunctionalComponent() {
  return <input />;
}
 
class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = React.createRef();
  }
  render() {
    // 這樣沒用!函數式組件根本就沒有實例!
    return (
      <MyFunctionalComponent ref={this.textInput} />
    );
  }
}

但是,你可以在函數式組件中使用ref屬性,就像你引用DOM元素和類組件一樣。

function CustomTextInput(props) {
  // textInput 必須被聲明在這里——ref才能適用於它
  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節點。通常來說我們不建議這樣做,因為這樣破壞了組件的封裝性,但是在某些情況下對於類似:觸發聚焦、改變子元素DOM節點的大小、位置等情況非常有用。

    你可以向子組件增加ref(就像上面說的),但是這並不是一個完美的解決方案——你只會獲得一個組件實例而不是DOM節點。更糟糕的是,它對函數式組件沒用!

    如果你使用React 16.3或者更高的版本,我們建議你在這些情況下使用ref forwarding,Ref fprwarding讓組件可以選擇去暴露子組件的ref作為他們自己的。你可以在ref forwarding文檔中找到更全面的例子——怎樣暴露子元素的DOM節點給父元素。

    如果你使用React 16.2或者更低的版本,或者你需要比ref forwarding所能提供的更多的靈活性。你可以使用替代方法,並且顯式的傳入一個ref當做一個不同命名的prop。

    如果可能,我們不建議暴露DOM節點,但是在一些情況下還是非常有用的。注意,這種方法需要你去在子組件中增加一些代碼,如果你完全沒有對於子組件實現的控制,你最后的選擇是使用findDOMNode()方法,當然,也只能這樣了。

(六)回調Refs

    React同樣支持另一種名為“回調refs”的方法去設置refs——它可以給我們對refs創建和銷毀更細粒度的控制。

    放入一個函數,而不是一個由createRef()創建的ref屬性。這個函數接受React組件實例、或者HTML DOM元素作為參數——可以被儲存並且在其他地方被訪問。

    下面的例子實現了一個通常的模式:使用ref回調儲存一個DOM節點的應用在實例變量中:

class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
 
    this.textInput = null;
 
    this.setTextInputRef = element => {
      this.textInput = element;
    };
 
    this.focusTextInput = () => {
      // 通過原生DOM API聚焦文本
      if (this.textInput) this.textInput.focus();
    };
  }
 
  componentDidMount() {
    // 在掛載時自動聚焦
    this.focusTextInput();
  }
 
  render() {
    // 使用'ref'回調去在一個實例域中儲存文本輸入DOM元素的引用(比如, this.textInput).
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

   React將會在組件掛載時使用DOM元素調用ref回調,在組件卸載時使用null調用ref回調。ref回調都會在componentDidMount或者componentDidUpdate生命周期鈎子之前被調用。

    你可以在組件之間傳遞回調refs,就像你可以對通過React.createRef()創建的對象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組件將他的ref回調作為inputRef這個屬性(props)傳入CustomTextInput組件,接着CustomTextInput組件將同樣的函數作為一個特殊的ref屬性(attribute)傳給<input>。從結果來看,Parent組件中的this.inputElement將會被放在與在CustomTextInput組件的<input>元素相關的DOM節點中。

(七)歷史遺留的API:字符串Refs

    不用管,以后都要移除(見到this.refs.textInput的形式,就使用React.createRef()或者回調模式代替)。

(八)關於回調refs的警告

    如果ref回調被定義為一個行內函數,當組件更新時會被調用兩次——第一次被null調用、而后被DOM元素調用。這是因為函數的新實例會在每次渲染的時候創建,所以React需要清除老的ref然后生成一個新的。你可以通過在class中定義一個綁定的ref回調方法避免這個問題,但是注意,這種問題在大多數情況下都沒什么影響~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM