React: useCallback React.memo
useCallback
的作用只是在依赖不变的情况下不返回新的函数地址,只使用旧的函数地址,函数仍然会重新创建.
React.memo
- 校验子组件的 props 的数据内存地址是否发生变化,从而决定是否渲染组件。所以 React.memo 经常需要搭配 useCallback 使用
useCallback 使用场景
- 向使用了 React.memo 的子组件传入 函数 (变量没有使用 useCallback 的必要).
缓存函数传递给 memo 组件
'use client';
import { memo, useRef, useEffect, useState, useCallback, useMemo } from 'react';
// https://stackoverflow.com/questions/63267099/how-set-displayname-to-a-react-stateless-component-with-memo
// https://stackoverflow.com/questions/73362190/usage-of-react-memo-inside-components-with-prop-functions
// https://zh-hans.react.dev/reference/react/useCallback#:~:text=%E5%9C%A8%20JavaScript%20%E4%B8%AD%EF%BC%8Cfunction%20()%20%7B%7D%20%E6%88%96%E8%80%85%20()%20%3D%3E%20%7B%7D%20%E6%80%BB%E6%98%AF%E4%BC%9A%E7%94%9F%E6%88%90%E4%B8%8D%E5%90%8C%E7%9A%84%E5%87%BD%E6%95%B0%E3%80%82
// memo and useCallback should be used together
const Clock = memo(function clock({ onClick }: { onClick: () => string }) {
const time = onClick();
return <div suppressHydrationWarning>{time}</div>;
});
export default function Home() {
const [render, setRender] = useState(false);
const [hasMounted, setHasMounted] = useState(false);
const getTime = useCallback(() => {
const time = new Date().getTime().toString();
return time;
}, []);
// 作为属性返回
const clockUi = useMemo(() => {
return <div>{new Date().getTime().toString()}</div>;
}, []);
useEffect(() => {
setHasMounted(true);
console.log('render home component');
}, []);
// https://www.joshwcomeau.com/react/the-perils-of-rehydration/
if (!hasMounted) {
return null;
}
return (
<>
<button onClick={() => setRender(!render)}>
更新状态 -- {JSON.stringify(render)}
</button>
<h2>useMemo</h2>
<div suppressHydrationWarning>{clockUi}</div>
<h2>useCallback and React.memo</h2>
<Clock onClick={getTime} />
</>
);
}
useMemo
- 调用函数并且缓存结果