一般情况下当函数作为依赖项时,应该使用 useCallback 来保证引用的稳定。例如组件的 props 或者是 useEffect 的依赖项。那假如函数被封装到一个 hook 中,那是都包一个 useCallback 好还是都不包,让使用者自已决定是否包?
一般情况下当函数作为依赖项时,应该使用 useCallback 来保证引用的稳定。例如组件的 props 或者是 useEffect 的依赖项。那假如函数被封装到一个 hook 中,那是都包一个 useCallback 好还是都不包,让使用者自已决定是否包?
在自定义 Hook 中主动使用 useCallback 是更好的实践,但需要遵循特定规则:
封装原则:Hook 应该自包含地管理自己的依赖关系,返回稳定引用
// 正确示例:在自定义 Hook 内部处理稳定性
function useCustomHook() {
const stableFn = useCallback(() => {
// 逻辑代码
}, [/* 正确依赖 */]);
return stableFn;
}
使用者注意事项:
// 错误用法:直接传递内联函数
const unstableValue = { id: 42 };
useCustomHook(unstableValue); // 会导致依赖数组失效
// 正确用法:使用者主动稳定依赖
const stableValue = useMemo(() => ({ id: 42 }), []);
useCustomHook(stableValue);
最佳实践路径:
这种设计既保证了 Hook 的封装性,又通过明确的依赖声明让使用者能够正确配合使用。
在hook 内用 useCallback
import { useState, useCallback } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const decrement = useCallback(() => {
setCount(prev => prev - 1);
}, []);
const reset = useCallback(() => {
setCount(initialValue);
}, [initialValue]);
return {
count,
increment,
decrement,
reset
};
}
页面里:
function CounterComponent() {
const { count, increment, decrement, reset } = useCounter(0);
useEffect(() =>
console.log('Counter setup');
return () => {
// 清理逻辑
};
}, [increment]); // 依赖项
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
<ChildComponent onIncrement={increment} />
</div>
);
}
不在 hook 内部用 useCallback
import { useState } from 'react';
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
// 不使用 useCallback
const increment = () => {
setCount(prev => prev + 1);
};
const decrement = () => {
setCount(prev => prev - 1);
};
const reset = () => {
setCount(initialValue);
};
return {
count,
increment,
decrement,
reset
};
}
页面里:
function CounterComponent() {
const { count, increment, decrement, reset } = useCounter(0);
const stableIncrement = useCallback(increment, [increment]);
const stableDecrement = useCallback(decrement, [decrement]);
const stableReset = useCallback(reset, [reset]);
useEffect(() => {
console.log('Counter setup');
//引用
}, [stableIncrement]);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
<ChildComponent onIncrement={stableIncrement} />
</div>
);
}
最简单的做法:
useCallback
,每次都声明就好,函数声明的开销其实很低。useRef
,那么也不需要。useCallback
。10 回答11k 阅读
6 回答2.9k 阅读
5 回答4.7k 阅读✓ 已解决
4 回答3k 阅读✓ 已解决
2 回答2.5k 阅读✓ 已解决
3 回答5k 阅读✓ 已解决
3 回答1.8k 阅读✓ 已解决
建议采用两个版本的函数。
或者通过选项参数提供灵活性:
这样可以在提供便利的同时保持灵活性,让使用者能根据自己的需求选择是否使用记忆化版本的函数。