5

I am working on a component where I read the query parameters from the URL and post a request inside useEffect, due to strict mode this is getting executed twice. The request is non-idempotent, what is the best way to handle this scenario?

I was thinking of maintaining a ref that contains whether the request was executed and not execute the request if it was already executed.

Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
king.reflex
  • 313
  • 4
  • 10
  • Hard to say what alternative pattern would make the most sense for your particular scenario. The React docs cover this in a lot of detail: [*You Might Not Need An Effect*](https://beta.reactjs.org/learn/you-might-not-need-an-effect) – Mulan Jan 25 '23 at 23:17
  • Are you sure you want to run an Effect everytime the query parameters of a URL change? That is exactly a not recommended case by React. It's exactly this link: https://react.dev/learn/synchronizing-with-effects#not-an-effect-buying-a-product Sending a Post request to an API should be more controlled with event handlers (interactions from the user) instead of relying on changing URLs (navigation). What if the user clicks the back button and comes back several times? In that case, the Effect would run as many times as the component mounts. – fjplaurr May 13 '23 at 08:04

3 Answers3

1

If you wish for your request to truly only run once per lifecycle of your application, you can track whether the request began with a top-level variable. It sounds too simple, but it does meet the requirements.

This is also recommended in react.dev

let didInit = false;

function App() {
  useEffect(() => {
    if (!didInit) {
      didInit = true;
      // ✅ Only runs once per app load
      loadDataFromLocalStorage();
      checkAuthToken();
    }
  }, []);
  // ...
}

I faced a similar issue when handling a code query string parameter as part of OAuth2.0 response_type=code response handling. I wanted to clear out the code from the query string, but when my component was re-mounted as part of React 18's new StrictMode behavior, I would not have the token response, or the code query string, so the second time my component mounted it looked like a fresh load where I should redirect to my login page.

useRef has a similar effect, but somehow feels MORE hacky. I suppose because it doesn't guarantee the same effect of having my code truly run only once per page load.

Acey
  • 8,048
  • 4
  • 30
  • 46
0

The docs recommend using a cleanup function instead. Rather than preventing it from firing twice, the cleanup function reverts the effects of the first execution, so that it appears as if the effect only ran once.

For example,

useEffect( () => {
  const dialog = dialogRef.current;
  dialog.showModal();

  // This will run after the first execution and close the modal that was just opened.
  // Then the second execution will re-open it, and it will remain open.
  return () => dialog.close();
}, [] );
Ian Dunn
  • 3,541
  • 6
  • 26
  • 44
0

The solution I would expect would be to use a react ref. It will work with a variable outside of the component, but I would prefer having any logic specific to the component inside itself.

Make sure to set the ref immediately instead of waiting for the request to be finished - otherwise, it will run twice in a react development environment.

export const MyComponent = () => {
  const initialRequestPerformed = React.useRef<boolean>(false);

  useEffect(() => {
    if (initialRequestPerformed.current) {
      return;
    }
    initialRequestPerformed.current = true;
    
    // send request
  }, []);

  return <></>
}
Jonas Grønbek
  • 1,709
  • 2
  • 22
  • 49