Patryk
Patryk

Reputation: 3152

Why is useState hook calling initialising value code twice?

Say we have the following code:

const [obj, setobj] = useState(generateInitialObj());

Why it happens that generateInitialObj() is being called twice? I've added logging to generateInitialObj() and I've just noticed it. It seems to be happening at the very begining of the component's life cycle. I though, that once react obtains a value from there, it's gonna remain in there for good. No need to re-obtain it. Any ideas?

Upvotes: 6

Views: 2004

Answers (3)

cbdeveloper
cbdeveloper

Reputation: 31395

Just tested and confirmed that if you use a function call getInitialState(), it will be called everytime, because it is, indeed, normal JS behavior.

function getInitialState() {
  console.log('Executing getInitialState...');
  return({
    propA: 'foo',
    propB: 'bar'
  });
}

function App() {
  console.log('Rendering App...');
  const [myState,setMyState] = React.useState(getInitialState());
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  function updateApp() {
    setMyBoolean((prevState) => !prevState);
  }
  
  function changeAppState() {
    setMyState({
      propA: 'foo2',
      propB: 'bar2'
    });
  }
  
  return(
    <React.Fragment>
      <div>myState.propA: {myState.propA}</div>
      <div>myState.propB: {myState.propB}</div>
      <button onClick={updateApp}>Update App</button>
      <button onClick={changeAppState}>Change App State</button>
    </React.Fragment>
  );
}


ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

But if you use a function declaration ()=>getInitialState() instead, it will only be called once.

function getInitialState() {
  console.log('Executing getInitialState...');
  return({
    propA: 'foo',
    propB: 'bar'
  });
}

function App() {
  console.log('Rendering App...');
  const [myState,setMyState] = React.useState(()=>getInitialState());
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  function updateApp() {
    setMyBoolean((prevState) => !prevState);
  }
  
  function changeAppState() {
    setMyState({
      propA: 'foo2',
      propB: 'bar2'
    });
  }
  
  return(
    <React.Fragment>
      <div>myState.propA: {myState.propA}</div>
      <div>myState.propB: {myState.propB}</div>
      <button onClick={updateApp}>Update App</button>
      <button onClick={changeAppState}>Change App State</button>
    </React.Fragment>
  );
}


ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

Other than that, both works just the same.

Upvotes: 1

I think re-rendering of component is the reason behind this. can you please post the component hierarchy?

Also try this:

const [obj, setobj] = useState(() => generateInitialObj());

here is a working example: https://stackblitz.com/edit/react-mysrjp

Upvotes: 0

August Lilleaas
August Lilleaas

Reputation: 54593

It's a JavaScript technicality. There's nothing React can do to prevent generateInitialObj from being called on every single render. To account for this, React supports the following API instead:

const [obj, setobj] = useState(() => generateInitialObj());

Upvotes: 13

Related Questions