0

I need to run a POST request only once when I enter the page, the purpose is to validate an user, depending on the response, different content will be shown on the page.

I'm using react-query to do the request, here's the example:

const { mutate } = useMutation({
  mutationFn: () => axios.post('/api/validate-users').then(res => res.data),
});

useEffect(() => {
  mutate({}, {
    onSuccess: (data) => {
      // Do something based on response data
    }
  })
}, [])

It works normally without StrictMode, but when it comes in, it makes the API to be called twice, in my use case I can't have this behavior. I read about it in React's documentation, but I can't have this validation attached to a button for example, I need it to run everytime I enter the page.

How can I have this API to be called only once with StrictMode?

I've tried to use React Refs, in order to prevent the API to be called twice. It worked, but it' such a big workaround, I feel like there's something easier to be done.

I know that in prod environment this shouldn't be an issue

tadman
  • 208,517
  • 23
  • 234
  • 262
  • You're working against the framework here.. You shouldn't be firing mutations on lifecycle events. You should be firing mutations in response to user actions. – Chad S. Aug 01 '23 at 15:04

1 Answers1

0

The behavior you are experiencing with the API being called twice is related to React's Strict Mode and the double render during development.

React's Strict Mode intentionally triggers two renders for components to help detect potential problems in the application. This can cause issues with some side effects, like making the API call twice in your case. The additional render is meant to catch unintended side effects and help developers identify and fix issues during development.

To make the API call only once, you can use the following approach:

Wrap the useEffect hook inside a useLayoutEffect hook. useLayoutEffect runs synchronously after all DOM mutations but before the browser paint. This way, you ensure that the effect runs before rendering to the screen.

To avoid the second render caused by React's Strict Mode, use a ref to keep track of whether the API call has already been made or not.

import { useState, useEffect, useLayoutEffect, useRef } from 'react';
import { useMutation } from 'react-query';
import axios from 'axios';

function MyComponent() {
  const [isAPICalled, setAPICalled] = useState(false);
  const isAPICalledRef = useRef(false); // Ref to track whether the API call has been made

  const { mutate } = useMutation({
    mutationFn: () => axios.post('/api/validate-users').then((res) => res.data),
  });

  // useLayoutEffect will run before the render (synchronously)
  useLayoutEffect(() => {
    if (!isAPICalledRef.current) {
      mutate({}, {
        onSuccess: (data) => {
          // Do something based on response data
        },
      });

      isAPICalledRef.current = true; // Mark the API call as made in the ref
      setAPICalled(true); // Update the state to trigger a render (optional)
    }
  }, [mutate]);

  // Render based on the API response and other logic
  return <div>{/* Your component content */}</div>;
}

export default MyComponent;
  • That's one of the workarounds I found, it will fire the API once. But this is not ideal, if I use Refs, the mutate callbacks (i.e.: `onSuccess`) will not trigger, because the component unmounts before the mutation has finished. ([TanStack blog](https://tkdodo.eu/blog/mastering-mutations-in-react-query#some-callbacks-might-not-fire)) I'm wondering if there's a way of prevent that without `useEffect` – Cassiano Matos Aug 01 '23 at 13:44