skip to Main Content

If a component is rendered. I want to open a new tab. (window.open(url, "_blank")?.focus();)
But (during development) this tab is opened twice because of the React.StrictMode.
How can I prevent this from being called multiple times without disabling the StrictMode?

My Attempts:

function MyComp() {
    useMemo(() => window.open(url, "_blank")?.focus(), []);
    return <div>...</div>;
}

(doesn’t work (called twice))

function MyComp() {
    useEffect(() => {
        const id = setTimeout(() => window.open(url, "_blank")?.focus(), 10);
        return () => clearTimeout(id);
    });
    return <div>...</div>;
}

(works, but seems not like a good solution)

2

Answers


  1. You can create this custom useEffectOnce hook in a separate file that will only execute code once, even during 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();
          }
        };
      }, []);
    };
    

    now instead of importing useEffect from React, import useEffectOnce from the directory you saved the hook in and do:

    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);
        });
    
    Login or Signup to reply.
  2. You simply need to keep a track of whether you have already opened the new tab.

    function MyComp() {
        const openedNewTabRef = useRef(false);
    
        useEffect(() => {
            if (!openedNewTabRef?.current) {
                window.open(url, "_blank")?.focus();
                openedNewTabRef.current = true;
            }
        }, []);
    
        return <div>...</div>;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search