近日,我在 react
中封装一个”划词翻译功能”时,遇到一个运用 ref
的问题。解决了很长时间。
咱们平常在运用 useRef
获取Dom元素,而且直接在 useEffect
中,立马就能够运用
function Ap() {
const ref = React.useRef();
React.useEffect(() => {
// 通常这里是能够直接获取到的
console.log(ref.current); // input Dom
}, []);
return (
<label>
<input type="text" ref={ref} />
</label>
);
}
但是,我遇到的情况是这样的(手撸代码,不必运转,好好看)
function App() {
const [state, setState] = useState(false);
useEffect(() => {
// 异步加载数据后
setState(true);
}, [])
const ref = useRef();
useEffect(() => {
// 拿不到了吧
console.log(ref.current); // undefined
}, []);
// jsx中条件未满意,所以没有createElement,所以拿不到ref
return (
<label>
{state && <input type="text" ref={ref} />}
</label>
);
}
那么,咱们在封装功能的时候,需要用 ref (一开始就要拿,且确保只拿一次) 怎么办呢?
-
运用
callbackRef
用回调函数的方法获取 ref,进而保存运用因为
callbackRef
会在实在Dom生成时履行
官网提示:
- React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.
function App() {
const [state, setState] = useState(false);
useEffect(() => {
// 异步加载数据后
setState(true);
}, [])
const domRef = useRef();
const onMouseUp = async (e) => {
console.log(e);
};
// 通过 callbackRef,在组件挂载后,操作该DOM
// 注意:还要用useCallback来包装函数,不是为了功能优化,
// 而是为了不生成新的回调函数让diff比照时发现差异,再次履行回调
const targetRef = useCallback((ref) => {
// 而且卸载组件时,会再次传入null调用该函数,会引发报错
// 所以:遇到null阻挠运转
if (!ref) return;
// 给dom绑定事件
ref.addEventListener("mouseup", onMouseUp);
// 保留ref,便于组件卸载时清除副作用
domRef.current = ref;
}, []);
useEffect(() => {
return () => {
// 清除事件
domRef.current.removeEventListener("mouseup", onMouseUp);
};
}, []);
// jsx中条件未满意,所以没有createElement,所以拿不到ref
return (
<label>
{state && <input type="text" ref={ref} />}
</label>
);
}
-
注意:
- 回调函数要用
useCallback
缓存,防止生成新的ref
让Diff比照时,认为发现新的差异 - 卸载组件时,会再次传入null调用
callbackRef
,要注意规避报错
- 回调函数要用
总结:
-
callbackRef
相较于 直接给值 的方法还是有一些特殊用途的。