本次要记录的是关于性能优化的一些hooks。
useCallback
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
返回一个回调函数。把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。
需求
来看一下我们的简单需求:要进行一个模糊搜索,那么就要进行防抖处理,代码如下
import React from "react";
import { useState } from "react";
function debounce(func, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func();
}, time);
};
}
const App = () => {
const [name, setName] = useState("");
const onChange = (e) => {
debounceSearch(e.target.value);
setName(e.target.value);
};
const search = () => {
console.log("debounce");
};
const debounceSearch = debounce(search, 1000);
return (
<div>
<input onChange={onChange} value={name}></input>
</div>
);
};
export default App;

显然我们的防抖失效了。
产生原因
经过分析我们可以得知,在每次输入的时候我们都要改变state,那么整个函数组件就进行重新的渲染,那么每次点击的debounceSearch处理函数都是新的,开启的定时器都不在同一个函数中,闭包也就失效了。
解决方法
究其原因就是每次debounceSearch在状态改变后就换了一个函数,我们只要保持这个函数不变就行了,最简单的方法就是把它定义为全局函数,这样组件刷新的时候它也不会刷新:
import React from "react";
import { useState } from "react";
function debounce(func, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func();
}, time);
};
}
const search = () => {
console.log("发送了请求");
};
const debounceSearch = debounce(search, 1000);
const App = () => {
const [name, setName] = useState("");
const onChange = (e) => {
debounceSearch(e.target.value);
setName(e.target.value);
};
return (
<div>
<input onChange={onChange} value={name}></input>
</div>
);
};
export default App;

显然我们的问题解决了。可是我要是不想提升呢?出于结构化的考虑,我想让同一部分的功能在一起,那么我们就要想办法让我们的函数缓存起来,这里就要用到我们的useCallBack函数了。
import React from "react";
import { useCallback } from "react";
import { useState } from "react";
function debounce(func, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func();
}, time);
};
}
const App = () => {
const [name, setName] = useState("");
const onChange = (e) => {
debounceSearch(e.target.value);
setName(e.target.value);
};
const search = () => {
console.log("发送了请求");
};
const debounceSearch = useCallback(debounce(search, 1000),[])
return (
<div>
<input onChange={onChange} value={name}></input>
</div>
);
};
export default App;
同样实现了我们的需求。由于useCallback(fn, deps) 相当于 useMemo(() => fn, deps),所以以下的效果是一样的。
import React from "react";
import { useMemo } from "react";
import { useCallback } from "react";
import { useState } from "react";
function debounce(func, time) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(() => {
func();
}, time);
};
}
const App = () => {
const [name, setName] = useState("");
const onChange = (e) => {
debounceSearch(e.target.value);
setName(e.target.value);
};
const search = () => {
console.log("发送了请求");
};
const debounceSearch = useMemo(()=>debounce(search, 1000),[])
return (
<div>
<input onChange={onChange} value={name}></input>
</div>
);
};
export default App;
总结
当我们组件的状态进行改变的时候,我们的函数就会重新执行,这个时候有些函数在功能的要求上不能让其改变此时就要用到今天学习的钩子进行缓存useCallback或者useMemo。