1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 3845字,需瀏覽 8分鐘

         ·

        2020-09-23 22:16

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

        來源: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 基本語法


        見鏈接: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í)行:
        首次無腦執(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,可以簡單的用useState模擬useRef:


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



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


        -?END -

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲欧洲精品成人久久奇米网 | 中国xxxxxxxxx老师 | 三女一男做爰3 | 少妇毛片久久久久久久久竹菊影院 | 在线探花 | 我才16就同学破了处好爽 | 久久久免费看片 | www.啪| 国产美女菊爆在线观看 | 天天激情站|