介绍
在 React 中,每次组件重新渲染时,函数都会被重新创建。如果这些函数被传递给子组件,可能会导致子组件不必要的重新渲染(即使子组件的 props 没有变化)。useCallback 可以缓存函数,确保在依赖项不变的情况下,函数引用保持不变。
基本用法
useCallback
接受两个参数:
- 函数:需要缓存的函数。
- 依赖项数组:当依赖项发生变化时,函数会重新创建。
const memoizedCallback = useCallback(
() => {
// 函数逻辑
},
[依赖项], // 依赖项数组
);
使用场景
场景 1:避免子组件不必要的渲染: 当父组件传递一个函数给子组件时,使用 useCallback 可以避免子组件因父组件重新渲染而重新渲染。
import React, { useCallback, useState } from 'react';
function Child({ onClick }) {
console.log('Child rendered');
return <button onClick={onClick}>Click me</button>;
}
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []); // 空依赖项数组,函数不会重新创建
return (
<div>
<p>Count: {count}</p>
<button onClick={()=> setCount(count + 1)}>Increment</button>
<Child onClick={handleClick} />
</div>
);
}
场景 2:依赖项变化时重新创建函数, 当函数依赖某些状态或 props
时,可以将这些依赖项添加到依赖项数组中。
import React, { useCallback, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Count:', count);
}, [count]); // 当 count 变化时,函数会重新创建
return (
<div>
<p>Count: {count}</p>
<button onClick={()=> setCount(count + 1)}>Increment</button>
<button onClick={handleClick}>Log Count</button>
</div>
);
}
注意事项
- 依赖项数组:
- 如果依赖项数组为空([]),函数只会在组件挂载时创建一次。
- 如果依赖项数组包含变量,当这些变量变化时,函数会重新创建。
- 性能优化:
- 只有在确实需要优化性能时使用 useCallback。过度使用可能会导致代码复杂化。
- 与 useMemo 的区别:
- useCallback 缓存的是函数,而 useMemo 缓存的是函数的返回值。
示例:结合 useMemo
使用
import React, { useCallback, useMemo, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Count:', count);
}, [count]);
const memoizedValue = useMemo(() => {
return count * 2; // 缓存计算结果
}, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Memoized Value: {memoizedValue}</p>
<button onClick={()=> setCount(count + 1)}>Increment</button>
<button onClick={handleClick}>Log Count</button>
</div>
);
}
注意事项
- 浅比较的限制:
React.memo
默认使用浅比较,如果props
是对象或数组,浅比较可能无法检测到内部变化。- 对于复杂
props
,建议使用自定义比较函数。
- 不要滥用
React.memo
:- 对于简单组件,
React.memo
可能不会带来明显的性能提升,反而会增加比较的开销, 因为比较 props 本身也需要计算开销,如果组件经常接收不同的 props,使用 memo 反而会增加额外开销 - 只有在组件渲染开销较大或 props 变化较少时,才建议使用 React.memo。
- 对于简单组件,
- 与
useMemo
和useCallback
结合使用:- 如果 props 包含函数或复杂对象,可以使用
useCallback
或useMemo
来避免不必要的重新创建。
- 如果 props 包含函数或复杂对象,可以使用
import React, { useState, memo, useCallback } from 'react';
const Child = memo(function Child({ onClick }) {
console.log('Child rendered');
return <button onClick={onClick}>Click Me</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return (
<div>
<button onClick={()=> setCount(count + 1)}>Increment</button>
<p>Count: {count}</p>
<Child onClick={handleClick} />
</div>
);
}
export default Parent;
总结
useCallback
用于缓存函数,避免在组件重新渲染时创建新的函数实例。- 适用场景:优化函数传递、避免子组件不必要的渲染。
- 依赖项数组:控制函数何时重新创建。
- 与
useMemo
的区别:useCallback
缓存函数,useMemo
缓存函数的返回值。