- base::useEffect
梗概
useEffect 无法直接监听 ref 值的变化,这是由于 React 的依赖比较机制决定的,需要采用其他方式处理 ref 变更监听需求。
原因说明
-
ref 对象比较特性
- React 的 useEffect 依赖数组使用浅比较(Object.is 或 ===)来检测变化
- ref 是一个包含 .current 属性的对象,即使内部 .current 值改变,ref 对象引用本身不变
- useEffect 无法感知到 ref.current 的变化
-
依赖追踪机制
- useEffect 只有在依赖数组中的变量引用发生变化时才会触发
- ref 对象在组件的整个生命周期中保持同一引用
解决方案
方案一:使用 state 配合
function RefWatcher() {
const myRef = useRef(initialValue);
const [refValue, setRefValue] = useState(initialValue);
// 当需要更新 ref 时,同步更新 state
const updateRefValue = (newValue) => {
myRef.current = newValue;
setRefValue(newValue); // 触发重新渲染并使 useEffect 可以监听
};
useEffect(() => {
console.log('Ref value changed:', refValue);
// 此处可执行需要监听 ref 变化的逻辑
}, [refValue]);
return (
// 组件内容
);
}方案二:使用 MutationObserver(DOM ref)
function DomRefWatcher() {
const domRef = useRef(null);
useEffect(() => {
if (!domRef.current) return;
const observer = new MutationObserver((mutations) => {
// 处理 DOM 变化
});
observer.observe(domRef.current, {
attributes: true,
childList: true,
subtree: true
});
return () => observer.disconnect();
}, []);
return <div ref={domRef}>{/* 内容 */}</div>;
}方案三:使用自定义 Hook
function useRefEffect(ref, callback, deps = []) {
useEffect(() => {
// 手动检测 ref.current 变化的逻辑
let prevValue = ref.current;
const checkForChanges = () => {
if (ref.current !== prevValue) {
callback(ref.current, prevValue);
prevValue = ref.current;
}
// 使用 requestAnimationFrame 或 setTimeout 周期性检查
requestId = requestAnimationFrame(checkForChanges);
};
let requestId = requestAnimationFrame(checkForChanges);
return () => cancelAnimationFrame(requestId);
}, deps);
}最佳实践
- 优先使用 state 而非 ref 来保存需要监听变化的数据
- 将需要触发副作用的逻辑与修改 ref 的逻辑放在同一个函数内
- 仅在不需要重新渲染的情况下使用 ref