As part of React Strict Mode, certain lifecycle functions will be ran twice, such as functions passed to useState, useMemo, or useReducer, or the whole body of a functional component, which might include your useEffect hook. If you’re unfamiliar with using hooks in React, check out our tutorial here.

While you can disable this, and I will still explain how, I’d consider avoiding doing so, and here’s why. If you’d like to skip the explanation, feel free to skip to the last section for a quick solution.

Side Effects

In React, you want your components to be pure, and free of unpredictable side effects. In React, side effects means your functions or components affecting anything outside of themselves.


React expects your functions to be “pure”, e.g. with the same arguments, they have the same results. For this reason it expects to be able to call your hooks twice, and they should have the same result. Even if they have a side-effect like performing an API call, it should cause the same result twice.

This is because outside of strict mode, React might run your hooks multiple times anyway, as it breaks the rendering phase up into pieces, and might pause or restart work. The best way to get around this is either to write your components with this in mind, or to explicitly check for when your hook has been ran.

Workaround

In some situations, you might really only want a hook to run once. This might be useful if for example you’re using the Spotify API, where you send a single-use code in order to receive an access token. To ensure this you can use the useRef hook, which you can read more about here, to keep track of whether or not to run your useEffect.

Here’s a simple example, our fetchData() function simply updates a counter when it has been ran. By default our counter gets updated twice, which isn’t what we want.

function App() { const [counter, setCounter] = useState(0); const fetchData = () => { console.log('Fetching data...'); setCounter((oldValue) => oldValue+1); } useEffect(() => { fetchData(); },[]) return ( <div className="App"> <h1>Times Fetched:</h1> <h1>{counter}</h1> </div> ); }

To get around this, we use useRef to keep track of a boolean. If it’s true, we’ve already been here, and we can return. If it is false, we can perform our fetch and then update the ref.

function App() { const [counter, setCounter] = useState(0); const dataFetchedRef = useRef(false); const fetchData = () => { console.log('Fetching data...'); setCounter((oldValue) => oldValue+1); } useEffect(() => { if (dataFetchedRef.current) return; dataFetchedRef.current = true; fetchData(); },[]) return ( <div className="App"> <h1>Times Fetched:</h1> <h1>{counter}</h1> </div> ); }

By the way, check out this tutorial if you’d like to see more on some actual data fetching in React.

Disabling React Strict Mode

If you’ve created your React app using create-react-app, you’re likely to have this in your index.js file, or something similar.

const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );

Simply remove the <React.StrictMode> tags around the <App> tag, and this should disable strict mode for your app! You can also only include this tag in pages where you do want strict mode enabled, to opt-in on a page by page basis.

Another important thing to note is that you will only run into this problem in development mode – With a production build of your app strict mode will be disabled, and the hooks will not explicitly run twice. Like I said, I’d recommend keeping strict mode enabled, even if it is causing your effects to run twice, since its more likely to help catch actual issues in your app, but the choice is yours!

Hopefully by now I’ve solved your issue, and if so feel free to leave a comment below! Let me know if you have any suggestions, or if you simply liked the article!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK, with a passion for ReactJS. I'm an enthusiastic and hardworking junior developer, driven by a desire to learn and explore ideas.

💬 Leave a comment

Your email address will not be published.

We will never share your email with anyone else.