3

I use RTK Query for data fetching and I am having a small issue in one of the use cases.

I use a mutation to verify the email address of the app user, as follows:

// ...imports

const ConfirmEmail = () => {
  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    if (isUninitialized) {
      confirmEmail({
        email: params.get('email'), // from the verification link
        code: params.get('code'), // from the verification link
      })
        .unwrap()
        .then(() => {
            // handling success...
        })
        .catch((e) => {
            // handling error...
        });
    }
  });

  // ...other component code
}

The problem is that with StrictMode the mutation is running twice in development and it is causing a concurrency error at the API side. I see two network requests in the dev tools one is successful and the other is not and depending on which one runs first the component is showing inconsistent result.

I am aware that this will only happen during development and I tried to follow the instructions in the official react documentation and I tried to use fixedCacheKey as described here but I wasn't able to make this work with RTK Query so that I get a consistent result during development.

Is there a way, or am I missing something?


Edit: my question falls into the same category as this question but it is more specific to RTK Query. Because in RTK Query if a component tries to make the same query as an existing one, no request will be performed (as mentioned here). However, what I am trying to do is a mutation in order to confirm the email address of a user (following a click on a link sent by email). Mutations do not follow the same rule as mentioned here.

useEffect cleanup function wasn't helpful and this is not like buying a product example.

Based on the discussions below, the available options were:

  1. Using a query instead of a mutation. But this won't be semantically correct.
  2. Force the user to click -again- on a button to initiate the "confirm email" mutation. Which is repetitive and not user-friendly.
  3. Use a ref to track whether a mutation is already running. Which is what I implemented at the end as follows:
// ...imports

const ConfirmEmail = () => {
  const verifiying = useRef(false);

  const [params] = useSearchParams();

  const [confirmEmail, { isLoading, isSuccess, isUninitialized, error }] =
    useConfirmEmailMutation();

  useEffect(() => {
    const isVerifying = verifiying.current;

    if (isUninitialized) {
      verifiying.current = true;

      confirmEmail({
        email: params.get('email'), // from the verification link
        code: params.get('code'), // from the verification link
      })
        .unwrap()
        .then(() => {
            // handling success...
        })
        .catch((e) => {
            // handling error...
        });
    }
  });

  // ...other component code
}
Damascus
  • 145
  • 9
  • Does this answer your question? [Why useEffect running twice and how to handle it well in React?](https://stackoverflow.com/questions/72238175/why-useeffect-running-twice-and-how-to-handle-it-well-in-react) – Youssouf Oumar Feb 20 '23 at 16:17
  • @yousoumar, your answer was the first resource I came across when looking up this issue and it was very helpful! I edited my question to explain why the matter is more specific to RTK Query. – Damascus Feb 21 '23 at 07:04
  • Okay I see Damascus :) – Youssouf Oumar Feb 21 '23 at 07:52

1 Answers1

1

That's pretty much the point of this strictness check though: to show you that you are automatically doing a problematic api call.

In the future, React can choose to execute a useEffect with an empty dependency array on more occasions than just on first mount - for example in combination with the offscreen features they are working on.
This just tells you in advance that you are doing this in probably a dangerous place.

Can you maybe incorporate this api call in some kind of actual user interaction like a button click? That would be a lot safer going forward.

phry
  • 35,762
  • 5
  • 67
  • 81
  • Thank you @phry. Do you mean by "problematic" is that this might occur in production if react behaved differently with `useEffect` in the future? Or is this a wrong way to do it in the first place? - The same happens by the way even when including the dependency array. I guess a workaround to avoid a second click would be to use a query instead of a mutation because two queries with the same args will not run at the same time. However, this will be semantically incorrect because it is actually an update request. – Damascus Feb 19 '23 at 13:43
  • 1
    It means that in the future, assuming that "off-screen-api", when your user scrolls away from your component and back to your component, the component will "pause" and "unpause", and possibly reexecute every effect on "unpausing", as if the component would be mounted. Putting something that results in a change on the server this into an effect is just likely the wrong place. By user interaction I mean the click that lets the user navigate to the component in the first place - on the component you are displaying before. – phry Feb 19 '23 at 15:24
  • 1
    This is a really interesting and promising! In my case, I am sending a verification link to the user by email and there is no previous component. I think the only safe way in this case would be to request the user to click again on a button to force confirming verification. But I still think that having the option to not trigger a mutation with the same args in rtk query would be nice (as it is the case in queries). For example this can be used to prevent a user from submitting a form twice (when the submit button is not disabled in the first click). – Damascus Feb 19 '23 at 15:51
  • 1
    In your case I would probably use a `ref` to track if the mutation has already been sent. – phry Feb 19 '23 at 15:58