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>

        React.memo()、useCallback()、useMemo()區(qū)別及基本使用

        共 6656字,需瀏覽 14分鐘

         ·

        2021-11-19 20:55

        來源 | https://www.fly63.com/


        先來看個簡單的例子
        // Parent.jsximport react, { useState } from 'react';import Child from '../Child';
        function Parent() { const [parentCount, setParentCount] = useState(0); console.log('父組件重新渲染--------------'); return ( <div style={{ background: 'lightseagreen' }}> <Child /> <button type="button" onClick={() => { setParentCount(parentCount + 1); }}>父組件 +1</button> </div> );}
        export default Parent;


        // Child.jsximport React from 'react';
        function Child() { console.log('------------子組件重新渲染'); return ( <div style={{ background: 'pink', margin: '50px 0' }}> <button type="button">子組件</button> </div> );}
        export default Child;
        當(dāng)點擊父組件按鈕時,父組件的狀態(tài)父組件會被更新,導(dǎo)致這些父組件重渲染,子組件也會重渲染;而此時我們的子組件和父組件之間并沒有依賴關(guān)系,因此重復(fù)渲染是優(yōu)化掉的,可以使用React.memo 包裹子組件
        // Child.jsximport React from 'react';// ...other codeexport default React.memo(Child);

        React.memo(Comp[, fn])

        用于減少子組件的渲染

        React.memo 是一個高階組件(參數(shù)為組件,返回的是新組件的函數(shù)即為高階組件)

        對外部來說,React.memo 會檢查道具的變更,只有當(dāng)核心的道具發(fā)生變化時組件才會重新成型,紐扣我們再點擊父組件,子就不會膨脹了。

        React.memo 對復(fù)雜對象做淺層對比,可以通過過程第二個參數(shù)來控制對比

        第二個參數(shù)為一個增強渲染細(xì)節(jié)的函數(shù)。

        function MyComponent(props) {  /* 使用 props 渲染 */}function areEqual(prevProps, nextProps) {  /*  如果把 nextProps 傳入 render 方法的返回結(jié)果與  將 prevProps 傳入 render 方法的返回結(jié)果一致則返回 true,  否則返回 false  */}export default React.memo(MyComponent, areEqual);

        useMemo(fn[, DependentArray])

        用于每次組件重新渲染時重復(fù)進行復(fù)雜的計算,參數(shù)為一個函數(shù)和依賴項數(shù)組,返回函數(shù)和函數(shù)的調(diào)用結(jié)果。
        const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
        useMemo作用類似于VUE的計算(計算屬性),不同之處在于需要手動傳入依賴項,依賴當(dāng)項變更時會重新調(diào)用傳入的函數(shù),返回計算值。
        依賴項為空數(shù)組時則直接返回上次的計算結(jié)果。
        不依賴項時,每次組件刷新都會重新計算,應(yīng)該在代碼能正常運行的情況下將其作為一種優(yōu)化策略策略。
        修改下我們的例子,請注意這里包裹了子組件,保證測試時子組件重重渲染受重球的道具變化的影響。
        // Parent.jsximport React, { useState, useMemo } from 'react';import Child from '../Child';
        function Parent() { const [parentCount, setParentCount] = useState(0); const [otherCount, setOtherCount] = useState(0); console.log('父組件重新渲染--------------');
        // 一個復(fù)雜的計算 const computedFn = (a, b) => { console.log('----重新執(zhí)行了計算----'); return a + b; };
        const computedValue = useMemo(() => { return computedFn(parentCount, 1); }, [parentCount]);
        return ( <div style={{ background: 'lightseagreen' }}> <Child parentCount={parentCount} computedValue={computedValue} /> <button type="button" onClick={() => { setParentCount(parentCount + 1); }}>父組件 +1</button> <button type="button" onClick={() => { setOtherCount(otherCount + 1); }}>父組件 otherCount+1</button> </div> );}
        點擊第一個按鈕,依賴項更改,輸出重執(zhí)行了計算,點擊第二個按鈕,更改更改的不是計算值,因為依賴項,因此不會重計算,組件也不會重渲染。

        useCallback(fn[, DependentArray])

        需要傳遞給子組件的函數(shù),連接子組件的重復(fù)渲染,參數(shù)為一個函數(shù)和任選的依賴項數(shù)組,返回出入函數(shù)的記憶版本。
        // Parent.jsximport React, { useState } from 'react';import Child from '../Child';
        function Parent() { const [parentCount, setParentCount] = useState(0); const [otherCount, setOtherCount] = useState(0); console.log('父組件重新渲染--------------');
        const computedFn = () => { return parentCount + 1; };
        return ( <div style={{ background: 'lightseagreen' }}> <Child parentCount={parentCount} computedFn={computedFn} /> <button type="button" onClick={() => { setParentCount(parentCount + 1); }}>父組件 +1</button> <button type="button" onClick={() => { setOtherCount(otherCount + 1); }}>父組件 otherCount+1</button> </div> );}
        export default Parent;
        // Child.jsximport React from 'react';
        function Child(props) { const { computedValue, computedFn } = props; console.log('------------子組件重新渲染'); return ( <div style={{ background: 'pink', margin: '50px 0' }}> <div> 父組件傳入的計算結(jié)果: {computedValue} </div> <button type="button" onClick={computedFn}>子組件</button> </div> );}
        export default React.memo(Child);
        當(dāng)點擊第二個按鈕時,子組件就可以重新渲染
        給computedFn 加上useCallBack
        // Parent.jsximport React, { useState, useCallback } from 'react';
        // ...other code
        const computedFn = useCallback(() => { console.log(parentCount); return parentCount + 1; }, [parentCount]) ;
        // ...other code
        export default Parent;
        一次再點擊父組件第二個按鈕子組件,子組件不會變大,使用Callback的依賴項沒有變化,返回的是上一次渲染的函數(shù),因此組件子組件的道具沒有變,組件不會重渲染。
        需要是,被useCallback保存的函數(shù)內(nèi)部作用域也不會發(fā)生變化,因此,當(dāng)項數(shù)組為注意的時候,依賴useCallback的函數(shù)的內(nèi)部通過閉包取的組件內(nèi)的要素值始終不變。
        import React, { useState, useCallback } from 'react';import Child from '../Child';
        let a = 0;function Parent() { const [parentCount, setParentCount] = useState(0); const [otherCount, setOtherCount] = useState(0); console.log('父組件重新渲染--------------');
        const computedFn = useCallback(() => { // 依賴項為空,這里的打印值始終不變; // 因為組件state變化時會重新渲染整個組件,而這里parentCount取的始終是第一次渲染版本的值 console.log(parentCount); // 這里的打印值會實時更新,因為變量直接定義在組件外部,不受組件重新渲染影響 console.log(a); return parentCount + 1; }, []) ;
        return ( <div style={{ background: 'lightseagreen' }}> <Child parentCount={parentCount} computedFn={computedFn} /> <button type="button" onClick={() => { setParentCount(parentCount + 1); a += 1; }}>父組件 +1</button> <button type="button" onClick={() => { setOtherCount(otherCount + 1); }}>父組件 otherCount+1</button> </div> );}
        export default Parent;
        因為useCallback目的是減少子組件重渲染,所以需要搭配子組件的shouldComponentUpdate或React.memo一起使用優(yōu)化意義。
        以上是依賴項更改不希望的情況,當(dāng)依賴項更改的時候,使用回調(diào)的記憶效果就不好,可以使用引用依賴項解決。
        function Form() {  const [text, updateText] = useState('');  const textRef = useRef();
        useEffect(() => { textRef.current = text; // 把它寫入 ref });
        const handleSubmit = useCallback(() => { // ref 對象在組件的整個生命周期內(nèi)保持不變 // 從 ref 讀取它,current的變更不會引起組件的重新渲染,而函數(shù)內(nèi)部又能拿到正確的值 const currentText = textRef.current; alert(currentText); }, [textRef]);
        return ( <> <input value={text} onChange={e => updateText(e.target.value)} /> <ExpensiveTree onSubmit={handleSubmit} /> </> );}

        使用引用

        看看官方介紹

        const refContainer = useRef(initialValue);

        useRef 返回一個附屬的 ref 對象,其 .current 被初始化為粒子的參數(shù)(initialValue)。返回的 ref 對象在組件的整個生命周期內(nèi)保持不變

        可以理解為:使用引用創(chuàng)建的對象擁有當(dāng)前的屬性,這個像個盒子,啥可能存,包括DOM節(jié)點;返回的引用對象在組件的整個生命周期內(nèi)保持不變,即存在當(dāng)前的值組件模糊渲染影響,始終保持著一開始的引用;同時屬性的修改也不會觸發(fā)組件的渲染;這個屬性的最終效果是useRef的參數(shù)
        看看官方例子
        function TextInputWithFocusButton() {  const inputEl = useRef(null);  const onButtonClick = () => {    // `current` 指向已掛載到 DOM 上的文本輸入元素    inputEl.current.focus();  };  return (    <>      <input ref={inputEl} type="text" />      <button onClick={onButtonClick}>Focus the input</button>    </>  );}
        當(dāng)把useRef創(chuàng)建的對象傳給DOM元素的ref屬性時,將動態(tài)DOM元素的引用存入當(dāng)前屬性,這樣就可以通過ref對象直接操作DOM元素了。

        學(xué)習(xí)更多技能

        請點擊下方公眾號

        瀏覽 66
        點贊
        評論
        1收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        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>
            操操操逼逼逼逼 | 蓝莓 c到爽 H视频 | 妇欲性难耐bd在线观看莉娜色诱 | 爆操国产 | 亚洲AV无码专区在线观看播放 | 好爽~~~嗯~~~再快点 | 欧美激情四射操逼在线 | 黄文在线看 | 五月不婷婷深爱月天 | 操女人逼逼 |