State variable is changing when accessed from useEffect()

Viewed 56

I am declaring a new variable called name and initializing it with useState to Math.Random() I then log the variable in 2 places, once in the body of the component, and once in the useEfect of the component. What I'm seeing is that the variable is changing.

My name is 0.1869175357793944 here My name is 0.10608469707774937 after render

Am I doing something wrong here? I cannot figure out why it's behaving like this.

Here is my code:

const {useState} = React;
const {useEffect} = React;

const Example = () => {
  const [name, setName] = useState(Math.random());
  console.log(`My name is ${name} here`);

  useEffect(() => {
    console.log(`My name is ${name} after render`);
  }, [])

  return (
    <div>Look at console</div>
  );
};

// Render it
ReactDOM.render(
  <React.StrictMode>
  <Example/>
  </React.StrictMode>,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.min.js"></script>
<div id="react"></div>

4 Answers

What you are seeing is an unintentional side-effect from the console.log in the component body. When you render your app inside a React.StrictMode component some functions are double-invoked to help you detect these unexpected side-effects.

const Example = () => {
  const [name, setName] = useState(Math.random());
  console.log(`My name is ${name} here`); // <-- unexpected side effect!!

  useEffect(() => {
    console.log(`My name is ${name} after render`);
  }, [])

  return (
    <div>Look at console</div>
  );
};

StrictMode - Detecting Unexpected Side Effects

Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies (emphasis mine)
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

Additionally

Note:

Starting with React 17, React automatically modifies the console methods like console.log() to silence the logs in the second call to lifecycle functions. However, it may cause undesired behavior in certain cases where a workaround can be used.

The additional note explains why you only see ONE "My name is 0.1869175357793944 here" log instead of two, each with a different random number value.

Here's a codesandbox running React v16.13.0 where the second log has not been stifled yet. Notice two unexpected logs with different values. Notice also that the result of the second call is that you see when the component actually rendered, i.e. what you see logged in the useEffect hook.

Edit state-variable-is-changing-when-accessed-from-useeffect

My name is 0.859128653421811 here 
My name is 0.6861295664325067 here 
My name is 0.6861295664325067 after render

Actually, this behavior will only happen in the development mode, not in production. In production, it will show a single value.

Reason: React applications are wrapped with <React.StrictMode /> component.

In short, strict mode help to cache any issue that occurs during development.

Note: Strict mode checks are run in development mode only; they do not impact the production build. https://reactjs.org/docs/strict-mode.html

Just for testing, try to remove <React.StrictMode>...</React.StrictMode>. Your problem will not exist anymore in the development mode as well.

 ReactDOM.render(
  <Example/>
  document.getElementById("react")
);

I believe the problem is that you are assigning name a value of the Math.random function instead of the result of Math.random().

Try this:

const [name, setName] = useState(Math.random());

I don't sure but it may work for you

const {useState} = React;
const {useEffect} = React;
const number=Math.random()
const Example = () => {
  const [name, setName] = useState(number);
  console.log(`My name is ${name} here`);

  useEffect(() => {
    console.log(`My name is ${name} after render`);
  }, [])

  return (
    <div>Look at console</div>
  );
};

// Render it
ReactDOM.render(
  <React.StrictMode>
  <Example/>
  </React.StrictMode>,
  document.getElementById("react")
);
Related