原文地址: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
最好在须要重复计算时才使用,在其余场景应用不当可能会有反作用,关于这一块的内容推荐阅读本文末尾中的参考文章。