Refs和Refs 轉(zhuǎn)發(fā)

1吊宋、定義

在React 數(shù)據(jù)流中,props是父組件與子組件交互的唯一方式颜武。需要修改子組件璃搜,要使用新的props來(lái)重新渲染。
但有些情況下鳞上,需要我們強(qiáng)制修改子組件(被修改的子組件可能是一個(gè) React 組件的實(shí)例这吻,也可能是一個(gè) DOM 元素),這時(shí)就可以使用Refs篙议。
Refs 提供了一種方式唾糯,允許我們?cè)L問(wèn) DOM 節(jié)點(diǎn)或在 render 方法中創(chuàng)建的 React 元素。

2鬼贱、使用場(chǎng)景

下面是幾個(gè)適合使用 refs 的情況:
1.管理焦點(diǎn)移怯,文本選擇或媒體播放。
2.觸發(fā)強(qiáng)制動(dòng)畫(huà)这难。
3.集成第三方 DOM 庫(kù)舟误。
4.避免使用 refs 來(lái)做任何可以通過(guò)聲明式實(shí)現(xiàn)來(lái)完成的事情。

3姻乓、Refs API使用

3.1 創(chuàng)建Refs

Refs 使用 React.createRef() 創(chuàng)建的嵌溢,并通過(guò) ref 屬性附加到 React 元素。(在構(gòu)造組件時(shí)糖权,通常將 Refs 分配給實(shí)例屬性堵腹,以便可以在整個(gè)組件中引用它們)

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
3.2 訪(fǎng)問(wèn) Refs

當(dāng) ref 被傳遞給 render 中的元素時(shí)炸站,對(duì)該節(jié)點(diǎn)的引用可以在 ref 的 current 屬性中被訪(fǎng)問(wèn)星澳。

console.log(this.myRef.current);//打印出設(shè)置了refs 的節(jié)點(diǎn)元素

ref 的值根據(jù)節(jié)點(diǎn)的類(lèi)型而有所不同:
一、當(dāng) ref 屬性用于 HTML 元素時(shí)旱易,構(gòu)造函數(shù)中使用 React.createRef() 創(chuàng)建的 ref 接收底層 DOM 元素作為其 current 屬性禁偎。

 //使用React.createRef()創(chuàng)建refs
      class CustomTextInput extends React.Component {
        constructor(props) {
          super(props);
          // 創(chuàng)建一個(gè) ref 來(lái)存儲(chǔ) textInput 的 DOM 元素
          this.textInput = React.createRef();
          this.focusTextInput = this.focusTextInput.bind(this);
        }

        focusTextInput() {
          // 直接使用原生 API 使 text 輸入框獲得焦點(diǎn)
          // 注意:我們通過(guò) "current" 來(lái)訪(fǎng)問(wèn) DOM 節(jié)點(diǎn)
          this.textInput.current.value = "";
          this.textInput.current.focus();
        }

        render() {
          // 告訴 React 我們想把 <input> ref 關(guān)聯(lián)到
          // 構(gòu)造器里創(chuàng)建的 `textInput` 上
          return (
            <div>
              <input type="text" ref={this.textInput} />
              <input
                type="button"
                value="Focus the text input"
                onClick={this.focusTextInput}
              />
            </div>
          );
        }
      }

二、當(dāng) ref 屬性用于自定義 class 組件時(shí)阀坏,ref 對(duì)象接收組件的掛載實(shí)例作為其 current 屬性如暖。

 //為 class 組件添加 Ref
      class AutoFocusTextInput extends React.Component {
        constructor(props) {
          super(props);
          this.textInput = React.createRef();
        }

        componentDidMount() {
          //調(diào)用class組件的方法
          this.textInput.current.focusTextInput();
        }

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

你不能在函數(shù)組件上使用 ref 屬性,因?yàn)樗麄儧](méi)有實(shí)例忌堂,可但以在函數(shù)組件內(nèi)部使用 ref 屬性盒至。

 //使用React.createRef()創(chuàng)建refs
      class CustomTextInput extends React.Component {
        constructor(props) {
          super(props);
          // 創(chuàng)建一個(gè) ref 來(lái)存儲(chǔ) textInput 的 DOM 元素
          this.textInput = React.createRef();
          this.focusTextInput = this.focusTextInput.bind(this);
        }

        focusTextInput() {
          // 直接使用原生 API 使 text 輸入框獲得焦點(diǎn)
          // 注意:我們通過(guò) "current" 來(lái)訪(fǎng)問(wèn) DOM 節(jié)點(diǎn)
          this.textInput.current.value = "";
          this.textInput.current.focus();
        }

        render() {
          // 告訴 React 我們想把 <input> ref 關(guān)聯(lián)到
          // 構(gòu)造器里創(chuàng)建的 `textInput` 上
          return (
            <div>
              <input type="text" ref={this.textInput} />
              <input
                type="button"
                value="Focus the text input"
                onClick={this.focusTextInput}
              />
            </div>
          );
        }
      }

4、回調(diào) Refs

React 也支持另一種設(shè)置 refs 的方式,稱(chēng)為“回調(diào) refs”枷遂。它能助你更精細(xì)地控制何時(shí) refs 被設(shè)置和解除樱衷。
不同于傳遞 createRef() 創(chuàng)建的 ref 屬性,你會(huì)傳遞一個(gè)函數(shù)酒唉。這個(gè)函數(shù)中接受 React 組件實(shí)例或 HTML DOM 元素作為參數(shù)矩桂,以使它們能在其他地方被存儲(chǔ)和訪(fǎng)問(wèn)。

    //回調(diào)Refs
      class MyComponent extends React.Component {
        constructor(props) {
          super(props);
          this.myRef = null;
        }
        componentDidMount() {
          this.myRef.innerHTML = "我是回調(diào)Refs";
          console.log(this.myRef);
        }
        render() {
          return (
            <div
              ref={element => {
                this.myRef = element;
              }}
            />
          );
        }
      }

可以在組件間傳遞回調(diào)形式的 refs痪伦,就像你可以傳遞通過(guò) React.createRef() 創(chuàng)建的對(duì)象 refs 一樣侄榴。

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

class Parent extends React.Component {
  render() {
    return (
      <MyComponent
        inputRef={el => this.inputElement = el}
      />
    );
  }
}

5、String 類(lèi)型的 Refs(老版本React中的API网沾,已經(jīng)過(guò)時(shí)癞蚕,不建議使用)

//過(guò)時(shí)的Refs
      class MyComponent extends React.Component {
        constructor(props) {
          super(props);
        }
        componentDidMount() {
          this.refs.myRef.innerHTML = "我是過(guò)時(shí)的String 類(lèi)型的Refs";//通過(guò) this.refs.XXXX來(lái)訪(fǎng)問(wèn) DOM 節(jié)點(diǎn)
          console.log(this.refs.myRef);
        }
        render() {
          return <div ref="myRef" />;
        }
      }

6、將 DOM Refs 暴露給父組件

在極少數(shù)情況下绅这,你可能希望在父組件中引用子節(jié)點(diǎn)的 DOM 節(jié)點(diǎn)涣达。通常不建議這樣做,因?yàn)樗鼤?huì)打破組件的封裝证薇。在新版本中可以使用Refs 轉(zhuǎn)發(fā)度苔,老版本可以將refs作為一個(gè)參數(shù),放在props中直接傳遞(如下例)

 function MyComponent(props) {//作為props的一員直接傳遞
        return (
          <div>
            <input
              ref={props.inputRef}
              onChange={null}
            />
          </div>
        );
      }
6.1 Refs 轉(zhuǎn)發(fā)

Ref 轉(zhuǎn)發(fā)是一項(xiàng)將 refs自動(dòng)地通過(guò)組件傳遞到其一子組件的技巧浑度。對(duì)于可重用的組件庫(kù)是很有用的寇窑。
我們需要使用API React.forwardRef 來(lái)獲取傳遞給它的 ref,然后轉(zhuǎn)發(fā)到它渲染的 DOM 箩张。

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

這樣甩骏,使用 MyButton 的組件可以獲取底層 DOM 節(jié)點(diǎn) button 的 ref ,并在必要時(shí)訪(fǎng)問(wèn)先慷,就像其直接使用 DOM button 一樣饮笛。
注意:
第二個(gè)參數(shù) ref 只在使用 React.forwardRef 定義組件時(shí)存在。常規(guī)函數(shù)和 class 組件不接收 ref 參數(shù)论熙,且 props 中也不存在 ref福青。
Ref 轉(zhuǎn)發(fā)不僅限于 DOM 組件,你也可以轉(zhuǎn)發(fā) refs 到 class 組件實(shí)例中脓诡。

   class App extends React.Component {
        constructor(props) {
          super(props);
          // 你可以直接獲取 DOM button 的 ref:
          this.ref = React.createRef();
        }

        componentDidMount() {
          setTimeout(() => {
            this.ref.current.innerText = "Refs 轉(zhuǎn)發(fā)";
          }, 2000);
          console.log(this.ref.current);

        }
        render() {
          return (
            <div>
              <MyButton ref={this.ref}>Click me!</MyButton>
            </div>
          );
        }
      }
  ReactDOM.render(<App />, document.getElementById("root"));

如圖无午,在父組件打印出了refs內(nèi)容,并且2秒改了了button上的內(nèi)容祝谚。

6.2 在高階組件中轉(zhuǎn)發(fā) refs

利用高階組件宪迟,就可以轉(zhuǎn)發(fā) refs 到 class 組件實(shí)例中。什么是高階組件呢交惯?
高階組件是一個(gè)函數(shù)次泽,它接受一個(gè)組件并返回一個(gè)新組件穿仪。

     class TextInput extends React.Component {
        constructor(props) {
          super(props);
          this.textInput = React.createRef();
        }
        setInputValue(value){
          this.textInput.current.value=value;
        }
        render() {
          return (<div><input ref={this.textInput} /></div>);
        }
      }

      class App extends React.Component {
        constructor(props) {
          super(props);
          // 你可以直接獲取 DOM button 的 ref:
          this.ref = React.createRef();
          this.HOCref = React.createRef();
        }

        componentDidMount() {
          setTimeout(() => {
            this.ref.current.innerText = "Refs 轉(zhuǎn)發(fā)";
          }, 2000);
          console.log(this.ref.current);
          console.log(this.HOCref.current);
          this.HOCref.current.setInputValue('我是父組件設(shè)置的值');
        }
        render() {
          return (
            <div>
              <MyButton ref={this.ref}>Click me!</MyButton>
              <NewFancyButton ref={this.HOCref}>高階函數(shù)</NewFancyButton>
            </div>
          );
        }
      }
      const NewFancyButton = HOCFunc(TextInput);
     function HOCFunc(Component) {
        class HOCComponent extends React.Component {
          componentDidUpdate(prevProps) {
            console.log("old props:", prevProps);
            console.log("new props:", this.props);
          }

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

            // 將自定義的 prop 屬性 “forwardedRef” 定義為 ref
            return <Component ref={forwardedRef} {...rest} />;
          }
        }

        // 注意 React.forwardRef 回調(diào)的第二個(gè)參數(shù) “ref”。
        // 我們可以將其作為常規(guī) prop 屬性傳遞給 HOCComponent意荤,例如 “forwardedRef”
        // 然后它就可以被掛載到被 HOCComponent 包裹的子組件上牡借。
        return React.forwardRef((props, ref) => {
          return <HOCComponent {...props} forwardedRef={ref} />;
        });
      }

上面的示例有一點(diǎn)需要注意:refs將不會(huì)透?jìng)飨氯ァ_@是因?yàn)閞ef不是prop屬性袭异。就像key一樣钠龙,其被React進(jìn)行了特殊處理。如果你對(duì)HOC添加ref御铃,該ref將引用最外層的容器組件碴里,而不是被包裹的組件。
幸運(yùn)的是上真,我們可以使用React.forwardRefAPI明確地將refs轉(zhuǎn)發(fā)到內(nèi)部的TextInput組件咬腋。React.forwardRef接受一個(gè)渲染函數(shù),其接收props和ref參數(shù)并返回一個(gè)React節(jié)點(diǎn)睡互。



上圖中打印出了轉(zhuǎn)發(fā)后得到refs的Class組件TextInput根竿,并且在父組件中操作了子組件中的方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末就珠,一起剝皮案震驚了整個(gè)濱河市寇壳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌妻怎,老刑警劉巖壳炎,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逼侦,居然都是意外死亡匿辩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)榛丢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)铲球,“玉大人,你說(shuō)我怎么就攤上這事晰赞〖诓。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵宾肺,是天一觀的道長(zhǎng)溯饵。 經(jīng)常有香客問(wèn)我侵俗,道長(zhǎng)锨用,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任隘谣,我火速辦了婚禮增拥,結(jié)果婚禮上啄巧,老公的妹妹穿的比我還像新娘。我一直安慰自己掌栅,他們只是感情好秩仆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著猾封,像睡著了一般澄耍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晌缘,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天齐莲,我揣著相機(jī)與錄音,去河邊找鬼磷箕。 笑死选酗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岳枷。 我是一名探鬼主播芒填,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼空繁!你這毒婦竟也來(lái)了殿衰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盛泡,失蹤者是張志新(化名)和其女友劉穎播玖,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體饭于,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜀踏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掰吕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片果覆。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖殖熟,靈堂內(nèi)的尸體忽然破棺而出局待,到底是詐尸還是另有隱情,我是刑警寧澤菱属,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布钳榨,位于F島的核電站,受9級(jí)特大地震影響纽门,放射性物質(zhì)發(fā)生泄漏薛耻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一赏陵、第九天 我趴在偏房一處隱蔽的房頂上張望饼齿。 院中可真熱鬧饲漾,春花似錦、人聲如沸缕溉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)证鸥。三九已至僚楞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枉层,已是汗流浹背镜硕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留返干,地道東北人兴枯。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像矩欠,于是被迫代替她去往敵國(guó)和親财剖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • It's a common pattern in React to wrap a component in an ...
    jplyue閱讀 3,259評(píng)論 0 2
  • 40癌淮、React 什么是React躺坟?React 是一個(gè)用于構(gòu)建用戶(hù)界面的框架(采用的是MVC模式):集中處理VIE...
    萌妹撒閱讀 1,005評(píng)論 0 1
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,050評(píng)論 2 35
  • 3. JSX JSX是對(duì)JavaScript語(yǔ)言的一個(gè)擴(kuò)展語(yǔ)法, 用于生產(chǎn)React“元素”乳蓄,建議在描述UI的時(shí)候...
    pixels閱讀 2,809評(píng)論 0 24
  • u/U:表示unicode字符串不是僅僅是針對(duì)中文, 可以針對(duì)任何的字符串咪橙,代表是對(duì)字符串進(jìn)行unicode編碼。...
    年畫(huà)兒閱讀 494評(píng)論 0 0