Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

What is the correct way to initiate a side-effect on page load when using React Strict Mode?

When using Strict Mode useEffect is always invoked at least twice, i.e.

useEffect(() => {
  console.log('Hello, World!');
}, []);

The above code will print "Hello, World!" "Hello, World!".

This breaks how I have traditionally implemented , e.g. log out upon visiting /log-out page:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

export const LogOutPage = () => {
  const { logout } = useAuth();

  useEffect(() => {
    logout({
      onCompleted: () => {
        console.log('logged out');
      },
      variables: {},
    });
  }, []);

  return null;
};

useEffect will be invoked twice and so is onCompleted, causing "logged out" to be logged twice.

I thought this could be solved by using state to prevent useEffect from being called the second time…

export const LogOutPage = () => {
  const [loggedOut, setLoggedOut] = useState(false);

  const { logout } = useAuth();

  useEffect(() => {
    if (loggedOut) {
      return;
    }

    console.log('loggedOut: ', loggedOut);

    setLoggedOut(true);

    logout({
      onCompleted: () => {
        console.log('logged out');
      },
      variables: {},
    });
  }, [loggedOut]);

  return null;
};

However, because the initial value of loggedOut is false, it still executes that code path twice, i.e. it will wring "loggedOut: false" "loggedOut: false"

It would appear that the only way to prevent this is to trigger the desired side-effect by a change in state, i.e.

export const LogOutPage = () => {
  const [startedLogOut, setStartedLogOut] = useState(false);
  const [loggedOut, setLoggedOut] = useState(false);

  const { logout } = useAuth();

  useEffect(() => {
    // This will be invoked twice.
    // However, the operation is idempotent.
    setStartedLogOut(true);
  }, []);

  useEffect(() => {
    if (!startedLogOut) {
      return;
    }

    setLoggedOut(true);

    if (loggedOut) {
      return;
    }

    logout({
      onCompleted: () => {
        console.log('logged out');
      },
      variables: {},
    });
  }, [startedLogOut, loggedOut]);

  return null;
};

This indeed works, however, it feels convoluted, and it is not an pattern I’ve observed in the wild. Asking this question to confirm that this is how I should be implementing side-effects when using Strict Mode.

>Solution :

Those effects which should be run only once can be handled using the ref.

export default const LogOutPage = () => {
  const [loggedOut, setLoggedOut] = useRef(false);

  const { logout } = useAuth();

  useEffect(() => {
    if (loggedOut.current) {
      return;
    }

    console.log('loggedOut: ', loggedOut.current);
 
    loggedOut.current = true;

    logout({
      onCompleted: () => {
        console.log('logged out');
      },
      variables: {},
    });
  }, []);

  return null;
};

More reading.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading