<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>

          useRef使用細(xì)節(jié)

          共 3845字,需瀏覽 8分鐘

           ·

          2020-09-23 22:16

          作者:普拉斯強(qiáng)? ?

          來(lái)源:SegmentFault 思否社區(qū)?





          一、動(dòng)機(jī)


          • 函數(shù)組件訪問DOM元素;


          • 函數(shù)組件訪問之前渲染變量。



          函數(shù)組件每次渲染都會(huì)被執(zhí)行,函數(shù)內(nèi)部的局部變量一般會(huì)重新創(chuàng)建,利用useRef可以訪問上次渲染的變量,類似類組件的實(shí)例變量效果。




          1.2 函數(shù)組件使用createRef不行嗎?


          createRef主要解決class組件訪問DOM元素問題,并且最佳實(shí)踐是在組件周期內(nèi)只創(chuàng)建一次(一般在構(gòu)造函數(shù)里調(diào)用)。如果在函數(shù)組件內(nèi)使用createRef會(huì)造成每次render都會(huì)調(diào)用createRef:


          function?WithCreateRef()?{
          ??const?[minus,?setMinus]?=?useState(0);
          ??//?每次render都會(huì)重新創(chuàng)建`ref`
          ??const?ref?=?React.createRef(null);

          ??const?handleClick?=?()?=>?{
          ????setMinus(minus?+?1);
          ??};

          ??//?這里每次都是`null`
          ??console.log(`ref.current=${ref.current}`)

          ??useEffect(()?=>?{
          ????console.log(`denp[minus]>`,?ref.current?&&?ref.current.innerText);
          ??},?[minus]);

          ??return?(
          ????"App">
          ??????Num:?{minus}
          ??????Add
          ????

          ??);
          }




          二、使用


          2.1 基本語(yǔ)法


          見鏈接:https://reactjs.org/docs/hooks-reference.html#useref


          • 每次渲染useRef返回值都不變;


          • ref.current發(fā)生變化并不會(huì)造成re-render;


          • ref.current發(fā)生變化應(yīng)該作為Side Effect(因?yàn)樗鼤?huì)影響下次渲染),所以不應(yīng)該在render階段更新current屬性。



          2.2?不可以在render里更新ref.current值


          在Is there something like instance variables提到:


          Unless you’re doing lazy initialization, avoid setting refs during rendering — this can lead to surprising behavior. Instead, typically you want to modify refs in event handlers and effects.


          • 在render里更新refs導(dǎo)致什么問題呢?

          • 在異步渲染里render階段可能會(huì)多次執(zhí)行。


          const?RenderCounter?=?()?=>?{
          ??const?counter?=?useRef(0);
          ??
          ??//?counter.current的值可能增加不止一次
          ??counter.current?=?counter.current?+?1;
          ??
          ??return?(
          ????

          {`The?component?has?been?re-rendered?${counter.current}?times`}


          ??);
          }


          2.3?可以在render里更新ref.current值


          同樣也是在Is there something like instance variables提到的:


          Unless you’re doing lazy initialization, avoid setting refs during rendering — this can lead to surprising behavior. Instead, typically you want to modify refs in event handlers and effects.


          • 為啥lazy initialization卻可以在render里更新ref.current值?

          • 這個(gè)跟useRef懶初始化的實(shí)現(xiàn)方案有關(guān)。


          const?instance?=?React.useRef(null)
          if?(instance.current?==?null)?{
          ??instance.current?=?{
          ????//?whatever?you?need
          ??}
          }


          本質(zhì)上只要保證每次render不會(huì)造成意外效果,都可以在render階段更新ref.current。但最好別這樣,容易造成問題,useRef懶初始化畢竟是個(gè)特殊的例外。


          2.4ref.current不可以作為其他hooksuseMemo,?useCallback,?useEffect)依賴項(xiàng)


          ref.current的值發(fā)生變更并不會(huì)造成re-render, Reactjs并不會(huì)跟蹤ref.current的變化。


          function?Minus()?{
          ??const?[minus,?setMinus]?=?useState(0);
          ??const?ref?=?useRef(null);

          ??const?handleClick?=?()?=>?{
          ????setMinus(minus?+?1);
          ??};

          ??console.log(`ref.current=${ref.current?&&?ref.current.innerText}`)

          ??//?#1?uesEffect
          ??useEffect(()?=>?{
          ????console.log(`denp[ref.current]?>`,?ref.current?&&?ref.current.innerText);
          ??},?[ref.current]);

          ??//?#2?uesEffect
          ??useEffect(()?=>?{
          ????console.log(`denp[minus]>`,?ref.current?&&?ref.current.innerText);
          ??},?[minus]);

          ??return?(
          ????"App">
          ??????Num:?{minus}
          ??????Add
          ????

          ??);
          }


          本例子中當(dāng)點(diǎn)擊[Add]按鈕兩次后#1 uesEffect就不會(huì)再執(zhí)行了,如圖:



          原因分析:


          依賴項(xiàng)判斷是在render階段判斷的,發(fā)生在在ref.current更新之前,而useEffect的effect函數(shù)執(zhí)行在渲染之后。


          1、第一次執(zhí)行:
          首次無(wú)腦執(zhí)行,所以輸出:



          ref.current=null
          denp[ref.current]?>?Num:?0
          denp[minus]>?Num:?0

          并且此時(shí)ref.current為null,所以?#1 uesEffect相當(dāng)于useEffect(() => console.log('num 1'), [null])




          2、點(diǎn)擊[Add],第二次執(zhí)行:
          此時(shí)ref.current值為

          Num: 0

          ,所以?#1 uesEffect的依賴項(xiàng)發(fā)生變化,最終輸出:


          ref.current=Num:?0
          denp[ref.current]?>?Num:?1
          denp[minus]>?Num:?1

          此時(shí)?#1 uesEffect相當(dāng)于useEffect(() => console.log('num 1'), [

          Num: 0

          ])


          3、點(diǎn)擊[Add],第三次執(zhí)行:

          此時(shí)ref.current值為

          Num: 1

          ,所以?#1 uesEffect的依賴項(xiàng)沒有發(fā)生變化,故?#1 uesEffect的effect函數(shù)不會(huì)被執(zhí)行,最終輸出:


          ref.current=Num:?1
          denp[minus]>?Num:?2

          如果將ref.current作為依賴項(xiàng),eslint-plugin-react-hooks也會(huì)報(bào)警提示的:


          React Hook useEffect has an unnecessary dependency: 'ref.current'. Either exclude it or remove the dependency array. Mutable values like 'ref.current' aren't valid dependencies because mutating them doesn't re-render the component react-hooks/exhaustive-deps


          2.5?ref作為其他hooks(useMemo,?useCallback,?useEffect)依賴項(xiàng)


          ref是不變的,沒必要作為其他hooks依賴。


          三、原理



          本質(zhì)上是記憶hook,但也可作為data hook,可以簡(jiǎn)單的用useState模擬useRef:


          const?useRef?=?(initialValue)?=>?{
          ??const?[ref]?=?useState({?current:?initialValue});
          ??return?ref
          }



          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動(dòng)和交流。


          -?END -

          瀏覽 42
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  青娱乐澳门久久 | 国产精品视频免费看 | 日韩视频导航 | 欧美激情毛片 | 日韩中文字幕高清无码视频 |