原文地址:www.linxiangjun.com/memoization…css
最近在研究React Hook優化時,發現Memoization技術被官方普遍的使用,好比useCallback
和useMemo
這兩個API,分別用來返回函數的memoized版本和memoized值。Memoization其實並非什麼新技術,只是一種優化技巧,維基百科對Memoization的定義爲:html
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.react
簡單來講Memoization就是存儲前一個值,而後每次更新時都將新傳入的值和存儲的值做比較,而後根據比較結果是否相同來返回存儲的值仍是新的值。React中使用該技術主要是爲了不沒必要要的重複渲染。git
咱們用來優化Redux數據的reselect其實就使用了Memoization技術,這篇文章分析了reselect源碼,能夠學習reselect是若是使用該技術來優化Redux數據的。github
熟悉了Memoization定義後,咱們看下若是在實戰中使用這個技術。在React Hook中,useEffect
方法可使用該技術來優化組件的渲染。本例子能夠在CodeSandbox中查看:smile:。redux
咱們知道useEffect
的第二個數組參數做爲effect依賴存在,若是依賴數組發生改變,那麼useEffect
就會從新執行。useEffect
使用===
來比較數組參數是否相等,使用Object.assign
或者splice
這類會操做時會返回新的堆來存儲引用值,就會致使值自己沒有變化卻會重複執行useEffect
方法。爲了不這種狀況的發生,咱們來看下該若是來作。數組
首先,先來介紹下useRef
方法,這個方法和原來的createRef
很是的類似,都是建立一個可變的ref
對象。在函數組件中,由於ref
對象在組件的整個生命週期內保持不變,因此咱們能夠用它來存儲不會被組件re-render影響的值。因此,在下面的函數中,咱們使用ref
對象來存儲useEffect
中傳入的第二個數組參數。app
import React, { useRef } from "react";
import { isEqual } from "lodash";
function useDeepCompareMemoize(value) {
const ref = useRef();
if (!isEqual(value, ref.current)) {
ref.current = value;
}
return ref.current;
}
複製代碼
能夠看到我使用了lodash中的深比較方法isEqual
來對比兩個值,這個能夠根據需求自定義方法也能夠。dom
接下來編寫自定義Hook來使用useEffect
:函數
function useDeepCompareEffect(callback, dependencies) {
useEffect(callback, useDeepCompareMemoize(dependencies));
}
export default useDeepCompareEffect;
複製代碼
而後使用useDeepCompareEffect
代替useEffect
便可。
使用的完整代碼以下:
import React, { useState, useEffect, useRef } from "react";
import ReactDOM from "react-dom";
import useDeepCompareEffect from "./useDeepCompareEffect.js";
import "./styles.css";
const tomCat = {
name: "Tom",
race: "cat"
};
const jerryMouse = {
name: "Jerry",
race: "mouse"
};
function App() {
const [character, updateCharacter] = useState(tomCat);
const effectCount = useRef(0);
const deepCompareEffectCount = useRef(0);
useEffect(() => {
effectCount.current++;
}, [character]);
useDeepCompareEffect(() => {
deepCompareEffectCount.current++;
}, [character]);
const changeStar = () => {
const star = character.name === "Tom" ? jerryMouse : tomCat;
updateCharacter(star);
};
const assignObj = () => {
updateCharacter(Object.assign({}, character));
};
return (
<div className="App"> <p>Hello, useEffect</p> <p className="star"> {character.name} {character.race} {character.friend} </p> <p>useEffect count = {effectCount.current}</p> <p>deepCompareEffectCount count = {deepCompareEffectCount.current}</p> <p> <button onClick={changeStar}>Change star</button> </p> <p> <button onClick={assignObj}>Click</button> </p> </div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement); 複製代碼
一樣的,點擊Click按鈕,使用useEffect
方法每次都會執行,而使用useDeepCompareEffect
的只會執行一次。不過須要注意的是,該優化並非萬金油,而是要根據狀況來使用。由於深比較原本就會對性能形成必定的損耗,因此要按需使用。好比useCallback
和useMemo
最好在須要重複計算時才使用,在其餘場景應用不當可能會有反作用,關於這一塊的內容推薦閱讀本文末尾中的參考文章。