<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文搞懂 React ref

          共 4640字,需瀏覽 10分鐘

           ·

          2020-12-03 12:37


          最近也開始研究React,這篇文章主要是講述 Ref 相關(guān)的內(nèi)容,如有錯誤請指正。

          ref 的由來

          在典型的 React 數(shù)據(jù)流中,props 是父組件與子組件交互的唯一方式。要修改一個子組件,你需要使用新的 props 來重新渲染它。但是,在某些情況下,你需要在典型數(shù)據(jù)流之外強制修改子組件/元素。

          適合使用 refs 的情況:

          • 管理焦點,文本選擇或媒體播放。
          • 觸發(fā)強制動畫。
          • 集成第三方 DOM 庫。

          ref 的三種方式

          在 React v16.3 之前,ref 通過字符串(string ref)或者回調(diào)函數(shù)(callback ref)的形式進行獲取。

          ref 通過字符獲取:

          //?string?ref
          class?MyComponent?extends?React.Component?{
          ??componentDidMount()?{
          ????this.refs.myRef.focus();
          ??}

          ??render()?{
          ????return?<input?ref="myRef"?/>;
          ??}
          }

          ref 通過回調(diào)函數(shù)獲取:

          //?callback?ref
          class?MyComponent?extends?React.Component?{
          ??componentDidMount()?{
          ????this.myRef.focus();
          ??}

          ??render()?{
          ????return?<input?ref={(ele)?=>?{
          ??????this.myRef?=?ele;
          ????}}?/>;
          ??}
          }

          在 v16.3 中,經(jīng) 0017-new-create-ref 提案引入了新的 API:React.createRef

          ref 通過 React.createRef 獲取:

          //?React.createRef
          class?MyComponent?extends?React.Component?{
          ??constructor(props)?{
          ????super(props);
          ????this.myRef?=?React.createRef();
          ??}

          ??componentDidMount()?{
          ????this.myRef.current.focus();
          ??}
          ??
          ??render()?{
          ????return?<input?ref={this.myRef}?/>;
          ??}
          }

          將被移除的 string ref

          首先來具體說說 string ref,string ref 就已被詬病已久,React 官方文檔中如此聲明:"如果你目前還在使用 this.refs.textInput 這種方式訪問 refs ,我們建議用回調(diào)函數(shù)或 createRef API 的方式代替。",為何如此糟糕?

          最初由 React 作者之一的 dan abramov。發(fā)布于https://news.ycombinator.com/edit?id=12093234,(該網(wǎng)站需要梯子)。吐槽內(nèi)容主要有以下幾點:

          1. 3.string ref 不可組合。例如一個第三方庫的父組件已經(jīng)給子組件傳遞了 ref,那么我們就無法再在子組件上添加 ref 了。另一方面,回調(diào)引用沒有一個所有者,因此您可以隨時編寫它們。例如:

            /**?string?ref?**/
            class?Parent?extends?React.Component?{
            ??componentDidMount()?{
            ????//?可獲取到?this.refs.childRef
            ????console.log(this.refs);
            ??}
            ??render()?{
            ????const?{?children?}?=?this.props;
            ????return?React.cloneElement(children,?{
            ??????ref:?'childRef',
            ????});
            ??}
            }

            class?App?extends?React.Component?{
            ??componentDidMount()?{
            ????//?this.refs.child?無法獲取到
            ????console.log(this.refs);
            ??}
            ??render()?{
            ????return?(
            ??????<Parent>
            ????????<Child?ref="child"?/>
            ??????Parent>

            ????);
            ??}
            }
          2. 2.string ref 的所有者由當前執(zhí)行的組件確定。這意味著使用通用的“渲染回調(diào)”模式(例如react),錯誤的組件將擁有引用(它將最終在react上而不是您的組件定義renderRow)。

            class?MyComponent?extends?Component?{
            ??renderRow?=?(index)?=>?{
            ????//?string?ref?會掛載在?DataTable?this?上
            ????return?;

            ????//?callback?ref?會掛載在?MyComponent?this?上
            ????return??this['input-'?+?index]?=?input}?/>;
            ??}

            ??render()?{
            ????return?
            ??}
            }
          3. 3.string ref 不適用于Flow之類的靜態(tài)分析。Flow不能猜測框架可以使字符串ref“出現(xiàn)”在react上的神奇效果,以及它的類型(可能有所不同)。回調(diào)引用比靜態(tài)分析更友好。

          4. 4.string ref 強制React跟蹤當前正在執(zhí)行的組件。這是有問題的,因為它使react模塊處于有狀態(tài),并在捆綁中復(fù)制react模塊時導(dǎo)致奇怪的錯誤。在 reconciliation 階段,React Element 創(chuàng)建和更新的過程中,ref 會被封裝為一個閉包函數(shù),等待 commit 階段被執(zhí)行,這會對 React 的性能產(chǎn)生一些影響。

          關(guān)于這點可以參考 React 源碼 coerceRef 的實現(xiàn):

          在調(diào)和子節(jié)點得過程中,會對 string ref 進行處理,把他轉(zhuǎn)換成一個方法,這個方法主要做的事情就是設(shè)置 instance.refs[stringRef] = element,相當于把他轉(zhuǎn)換成了function ref

          對于更新得過程中string ref是否變化需要對比得是 current.ref._stringRef,這里記錄了上一次渲染得時候如果使用得是string ref他的值是什么

          owner是在調(diào)用createElement的時候獲取的,通過ReactCurrentOwner.current獲取,這個值在更新一個組件前會被設(shè)置,比如更新ClassComponent的時候,調(diào)用render方法之前會設(shè)置,然后調(diào)用render的時候就可以獲取對應(yīng)的owner了。

          堅挺的 callback ref

          React 將在組件掛載時,會調(diào)用 ref 回調(diào)函數(shù)并傳入 DOM 元素,當卸載時調(diào)用它并傳入 null。在 componentDidMount 或 componentDidUpdate 觸發(fā)前,React 會保證 refs 一定是最新的。

          如果 ref 回調(diào)函數(shù)是以內(nèi)聯(lián)函數(shù)的方式定義的,在更新過程中它會被執(zhí)行兩次,第一次傳入?yún)?shù) null,然后第二次會傳入?yún)?shù) DOM 元素。這是因為在每次渲染時會創(chuàng)建一個新的函數(shù)實例,所以 React 清空舊的 ref 并且設(shè)置新的。通過將 ref 的回調(diào)函數(shù)定義成 class 的綁定函數(shù)的方式可以避免上述問題,但是大多數(shù)情況下它是無關(guān)緊要的。

          最新的 React.createRef

          React.createRef 的優(yōu)點:

          • 相對于 callback ref 而言 React.createRef 顯得更加直觀,避免了 callback ref 的一些理解問題。

          React.createRef 的缺點:

          1. 性能略低于 callback ref
          2. 能力上仍遜色于 callback ref,例如上一節(jié)提到的組合問題,createRef 也是無能為力的。

          ref 的值根據(jù)節(jié)點的類型而有所不同:

          • 當 ref 屬性用于 HTML 元素時,構(gòu)造函數(shù)中使用 React.createRef() 創(chuàng)建的 ref 接收底層 DOM 元素作為其 current 屬性。
          • 當 ref 屬性用于自定義 class 組件時,ref 對象接收組件的掛載實例作為其 current 屬性。
          • 默認情況下,你不能在函數(shù)組件上使用 ref 屬性(可以在函數(shù)組件內(nèi)部使用),因為它們沒有實例:
            • 如果要在函數(shù)組件中使用 ref,你可以使用 forwardRef(可與 useImperativeHandle 結(jié)合使用)
            • 或者可以將該組件轉(zhuǎn)化為 class 組件。

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

          是否需要將 DOM Refs 暴露給父組件?

          在極少數(shù)情況下,你可能希望在父組件中引用子節(jié)點的 DOM 節(jié)點。通常不建議這樣做,因為它會打破組件的封裝,但它偶爾可用于觸發(fā)焦點或測量子 DOM 節(jié)點的大小或位置。

          如何將 ref 暴露給父組件?

          如果你使用 16.3 或更高版本的 React, 這種情況下我們推薦使用 ref 轉(zhuǎn)發(fā)。Ref 轉(zhuǎn)發(fā)使組件可以像暴露自己的 ref 一樣暴露子組件的 ref。

          什么是 ref 轉(zhuǎn)發(fā)?

          const?FancyButton?=?React.forwardRef((props,?ref)?=>?(
          ??<button?ref={ref}?className="FancyButton">
          ????{props.children}
          ??button>

          ));

          //?你可以直接獲取 DOM button 的 ref:
          const?ref?=?React.createRef();
          <FancyButton?ref={ref}>Click?me!FancyButton>;

          如果再低版本中如何轉(zhuǎn)發(fā)?

          如果你使用 16.2 或更低版本的 React,或者你需要比 ref 轉(zhuǎn)發(fā)更高的靈活性,你可以使用 ref 作為特殊名字的 prop 直接傳遞。

          比如下面這樣:

          function?CustomTextInput(props)?{
          ??return?(
          ????

          ??????
          ????

          ??);
          }

          class?Parent?extends?React.Component?{
          ??constructor(props)?{
          ????super(props);
          ????this.inputElement?=?React.createRef();
          ??}
          ??render()?{
          ????return?(
          ??????
          ????);
          ??}
          }

          以下是對上述示例發(fā)生情況的逐步解釋:

          1. 我們通過調(diào)用 React.createRef 創(chuàng)建了一個 React ref 并將其賦值給 ref 變量。
          2. 我們通過指定 ref 為 JSX 屬性,將其向下傳遞給
          3. React 傳遞 ref 給 forwardRef 內(nèi)函數(shù) (props, ref) => ...,作為其第二個參數(shù)。
          4. 我們向下轉(zhuǎn)發(fā)該 ref 參數(shù)到
          5. 當 ref 掛載完成,ref.current 將指向




          最后



          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 歡迎加我微信「qianyu443033099」拉你進技術(shù)群,長期交流學(xué)習(xí)...

          3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


          點個在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 78
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆精品无码久久久介绍 | 欧美三级片网站网址 | 亚洲欧美一级二级三级 | 大鸡吧综合网 | 亚洲偷偷 |