如果组件被渲染。我想打开一个新标签。(window.open(url, "_blank")?.focus();
)但是(在开发过程中)由于React.StrictMode,这个选项卡被打开了两次。我怎样才能防止这个被调用多次而不禁用StrictMode?
我的尝试:
function MyComp() {
useMemo(() => window.open(url, "_blank")?.focus(), []);
return <div>...</div>;
}
(行不通(调用两次))
function MyComp() {
useEffect(() => {
const id = setTimeout(() => window.open(url, "_blank")?.focus(), 10);
return () => clearTimeout(id);
});
return <div>...</div>;
}
(工作,但似乎不是一个好的解决方案)
您只需要跟踪您是否已经打开了新选项卡。
function MyComp() {
const openedNewTabRef = useRef(false);
useEffect(() => {
if (!openedNewTabRef?.current) {
window.open(url, "_blank")?.focus();
openedNewTabRef.current = true;
}
}, []);
return <div>...</div>;
}
您可以在单独的文件中创建此自定义useEffectOnce
钩子,该钩子只执行一次代码,即使在StrictMode
期间:
// ./src/hooks/useEffectOnce.tsx
import { useEffect, useRef, useState } from 'react';
export const useEffectOnce = (effect: () => void | (() => void)) => {
const effectFn = useRef<() => void | (() => void)>(effect);
const destroyFn = useRef<void | (() => void)>();
const effectCalled = useRef(false);
const rendered = useRef(false);
const [, setVal] = useState<number>(0);
if (effectCalled.current) {
rendered.current = true;
}
useEffect(() => {
if (!effectCalled.current) {
destroyFn.current = effectFn.current();
effectCalled.current = true;
}
setVal(val => val + 1);
return () => {
if (!rendered.current) {
return;
}
if (destroyFn.current) {
destroyFn.current();
}
};
}, []);
};
现在不是从React中导入useEffect
,而是从保存钩子的目录中导入useEffectOnce
,并执行:
import { useEffectOnce } from "../hook/useEffectOnce" // <-- the path where you saved the hook
[...]
useEffectOnce(() => {
const id = setTimeout(() => window.open(url, "_blank")?.focus(), 10);
return () => clearTimeout(id);
});