本文正在参加「金石计划」
const cachedFn = useCallback(fn, dependencies)
-
在重新渲染之间缓存函数定义
-
fn 可以接收任意参数,返回任意数据
-
初次渲染返回 fn, dependencies 没有改变返回相同fn, dependencies改变返回不同的fn
子组件跳过重新渲染
import { memo } from 'react';
const ShippingForm = memo(function ShippingForm({ onSubmit }) {
// ...
});
function ProductPage({ productId, referrer, theme }) {
// Tell React to cache your function between re-renders...
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails,
});
}, [productId, referrer]); // ...so as long as these dependencies don't change...
return (
<div className={theme}>
{/* ...ShippingForm will receive the same props and can skip re-rendering */}
<ShippingForm onSubmit={handleSubmit} />
</div>
);
}
缓存函数中更新 state(不建议)
使用 updater functions
Effect 触发太频繁(不建议)
把函数放在 Effect 里面
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
const createOptions = useCallback(() => {
return {
serverUrl: 'https://localhost:1234',
roomId: roomId
};
}, [roomId]); // ✅ Only changes when roomId changes
useEffect(() => {
const options = createOptions();
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // ✅ Only changes when createOptions changes
// ...
自定义 hook 使用 useCallback
把所有导出的函数使用 useCallback 包裹
useCallback 和 useMemo
useMemo 缓存调用函数的结果。
useCallback 缓存函数本身。
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
除了使用 useMemo,一些其他建议
- 组件作为 children 传递时
- 减少 state 使用和提升
- 渲染逻辑保持纯净
- 避免在 Effect 中更新 state
- 尽量减少 Effect dependencies
问答
-
组件每次渲染, useCallback 返回一个不同的函数
没有指定 dependencies 或者 dependencies 依赖项每次返回的数据都不一样
-
循环中调用 useCallback
提取循环内容为一个组件,在组件内部使用 useCallback, 可以缓存某个数据或者整个组件
function ReportList({ items }) { return ( <article> {items.map(item => { // 🔴 You can't call useCallback in a loop like this: const handleClick = useCallback(() => { sendReport(item) }, [item]); return ( <figure key={item.id}> <Chart onClick={handleClick} /> </figure> ); })} </article> ); } function ReportList({ items }) { return ( <article> {items.map(item => <Report key={item.id} item={item} /> )} </article> ); } function Report({ item }) { // ✅ Call useCallback at the top level: const handleClick = useCallback(() => { sendReport(item) }, [item]); return ( <figure> <Chart onClick={handleClick} /> </figure> ); } function ReportList({ items }) { // ... } const Report = memo(function Report({ item }) { function handleClick() { sendReport(item); } return ( <figure> <Chart onClick={handleClick} /> </figure> ); });