建議使用 useCallback 的時機
如果你的 function 因為需要用到 props 或 state 而必須在 component scope 裡面宣告、但又同時會被超過一個 useEffect
使用時,就建議以 useCallback
包起來。
這樣可以確保當 props 或 state 改變時,useCallback
先跟著改變、進而觸發 useEffect
的行為。
// When your callback are used in multiple useEffect()
function SearchResults({ query }) {
const getFetchUrl = useCallback(() => {
return 'https://hn.algolia.com/api/v1/search?query=' + query;
}, [query]);
useEffect(() => {
const url = getFetchUrl('react');
// ... Fetch data and do something ...
}, [getFetchUrl]);
useEffect(() => {
const url = getFetchUrl('redux');
// ... Fetch data and do something ...
}, [getFetchUrl]);
// ...
}
如果你傳給 useMemo
的 function 裡面運算很繁重,例如可能要 map 一組很大的陣列,這時候可能就很值得用 useMemo
把運算結果暫記起來下次用。
// Assume this returns an Array of 3000 records
const menuItemRows = useMemo(
() => thousandsOfMenuItems.map(menuItem => (
<MenuItemRow key={menuItem.uuid} name={menuItem.name} />
)),
[thousandsOfMenuItems]
);
const [count, setCount] = useState(1);
const consoleFunction = useCallback(() => {
console.log('consoleFunction');
}, []);
useEffect(() => {
setCount(count + 1);
console.log(`第二个参数: 函数, 第 ${count} 次执行`);
}, [consoleFunction]);
// 打印log,执行一次
第二个参数: 函数, 第 1 次执行
const [count, setCount] = useState(1);
const obj = useMemo(() => ({name: 'zhangsan'}), []);
useEffect(() => {
setCount(count + 1);
console.log(`第二个参数: 对象, 第 ${count} 次执行`);
}, [obj]);
// 打印log
第二个参数: 对象, 第 1 次执行
如果 react + ts, useCallback 必须传入第二个参数,useMemo 可选填
如果传入的第二个参数是一个 空数组,后续的渲染,只会记住第一次渲染的
If no array is provided, a new value will be computed on every render.
如果 useMemo 不传入参数,相当于没有 使用 useMemo, 每次都会重新 computed
误区一:
import { useCallback, useState } from "react";
const Child = (props) => {};
const App = () => {
const handleChange = useCallback(() => {}, []);
const [count, setCount] = useState(0);
return (
<>
<div
onPress={() => {
setCount(count + 1);
}}
>
increase
</div>
<Child handleChange={handleChange} />
</>
);
};
export default App;
误区二:
import { useCallback, useState, memo } from "react";
const Child = memo((props) => {});
const App = () => {
const handleChange = () => {};
const [count, setCount] = useState(0);
return (
<>
<div
onPress={() => {
setCount(count + 1);
}}
>
increase
</div>
<Child handleChange={handleChange} />
</>
);
};
export default App;
对于复杂的组件项目中会使用 memo 进行包裹,目的是为了对组件接受的 props 属性进行浅比较来判断组件要不要进行重新渲染。这当然是正确的做法,但是问题出在 props 属性里面有引用类型的情况,例如数组、函数,如果像上面这个例子中这样书写,handleChange 在 App 组件每次重新渲染的时候都会重新创建生成,引用当然也是不一样的,那么势必会造成 Child 组件重新渲染。所以这种写法也是白给的。