Search code examples
javascriptreactjscreate-react-appcodepencodesandbox

React application created using create-react-app increments module-scoped variable without reason


I have a React application created using create-react-app. I was testing something and created the following piece of code:

import React, {useState} from "react";

var a = 1;

function useForceUpdate() {
  const [state, setState] = useState(true);

  return [() => setState(!state)];
}

function App() {
  const [forceUpdate] = useForceUpdate();
  a++;
  console.log(a);

  return (
    <div onClick={forceUpdate}>
      {a}
    </div>
  );
}

export default App;

For some reason, it starts with a value of 3 and on every click it increments by 2 or 3 instead of 1, but that only happened on my already existing project which has additional libraries such as enzyme, react-hooks-testing-library and others. On a new project where only the App.js code was changed, it starts with 3 and always increments by exactly 2 on clicking. I tried it on codepen and there it starts with a value of 2 and increments by 1 as it should. Here is the codepen (replace the .js file here: https://reactjs.org/redirect-to-codepen/hello-world with the following code):

var a = 1;

function useForceUpdate() {
  const [state, setState] = React.useState(true);

  return [() => setState(!state)];
}

function App() {
  const [forceUpdate] = useForceUpdate();
  a++;
  console.log(a);

  return (
    <div onClick={forceUpdate}>
      {a}
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

And on codesandbox (just replace the .js file in this with the provided one below: https://codesandbox.io/s/new?file=/src/App.js) it again starts with a value of 3 and increments by 2 every time:

import React from "react";

var a = 1;

function useForceUpdate() {
  const [state, setState] = React.useState(true);

  return [() => setState(!state)]
}

function App() {
  const [forceUpdate] = useForceUpdate();
  a++;
  console.log(a);

  return (
    <div onClick={forceUpdate}>
      {a}
    </div>
  );
}

export default App;

Why do they all behave so differently?


Solution

  • This is happening because of React's Strict Mode.React's Strict Mode double invokes certain function in order to identify any unwanted side-effects in your app. You can read more about it here : React Strict Mode

    Now, if you go to the index.js file of your app you will see that the App is wrapped by Strict Mode. Similar is the case of index.js file in codesandbox.

    eg,

    const rootElement = document.getElementById("root");
    ReactDOM.render(
      <StrictMode>
        <App />
      </StrictMode>,
      rootElement
    );
    

    However in case of codepen, the code dosen't have any StrictMode wrapping hence, it adds only once to a.