简体   繁体   中英

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?

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.

It's about dividing files into modules, but I can't explain what exactly is happening under the hood. Most likely, connecting the module somehow just reads the file, rather than including its code in the main code. This will increment the variable.

You can easily check it:

  • First case - create component in separate file(module) and import it to root index.js
  • Second case - create component with call ReactDOM.render in one file (module)

And add console.log outside a component

  • In first case - you will see 3 logs, 2 outside a component and one inside.
  • In second case - 2 logs, 1 outside and 1 inside

And of course it starts with 3 in first case because you work with bundled code.

But it seems to me that this is wrong to define component-changing variables outside the component code or state manager

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM