使用ref解耦callback是否安全?React相关实现逻辑与适用问题咨询
useDecoupledCallback 功能说明
这个Hook接收一个通过useCallback创建的函数(useCallback会在其依赖项每次变更时更新自身引用),返回一个始终调用最新版本回调、且自身引用永远不会更新的函数。
部分场景下你需要关注回调的更新时机,但类似事件处理器的场景通常不需要。如果你把这个Hook的返回值传给不关心回调更新时机的memo子组件,还可以避免不必要的渲染。
import {useRef, useEffect, useCallback} from "react"; export default function useDecoupledCallback(callback) { const callbackRef = useRef(callback); useEffect(() => { callbackRef.current = callback; }, [callback]); return useCallback((...args) => callbackRef.current(...args), [callbackRef]); }
问题解答
问题1:当事件监听器的依赖项发生变更时,React会删除旧的事件监听器并替换为新的,还是内部也采用了和上述hook类似的处理逻辑?如果React没有采用类似逻辑,是否是因为React渲染速度足够快,这部分差异不会对性能产生影响?
- React 17及更早版本的合成事件系统,确实会在每次组件重渲染时解绑旧的事件监听器、绑定新的。这一操作的性能开销其实非常小,普通业务场景下几乎感知不到差异,所以官方早期没有做这类优化。
- 到了React 18,官方已经内置了类似的处理逻辑,内部就是通过ref存储最新的事件回调,避免每次重渲染都重新绑定事件监听器,性能更优。
问题2:假设该方案对性能没有负面影响,是否可以安全使用该方法来缩短依赖hook的依赖数组?
- 绝对不可以这么做。这个Hook的设计初衷只是用来处理事件回调、避免memo子组件不必要重渲染,不是用来逃避依赖规则检查的。
- 如果你把它用在
useEffect、useMemo这类需要明确感知依赖变化的Hook的依赖数组里,会出现闭包陷阱:因为这个Hook返回的函数引用永远不变,依赖数组感知不到回调的变化,会导致回调内部用到的状态永远是旧的,出现非常难排查的bug。 - 只有当你确定这个回调只会作为事件触发的处理器使用,不需要被其他响应式Hook感知变化的时候,才可以安全使用这个Hook。
内容的提问来源于stack exchange,提问作者BanForFun
相关产品推荐
相关产品推荐

