最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to avoid the useState function parameter (to get initial values) from being executed on every render? - Stack O

programmeradmin1浏览0评论

See form the snippet below that the getInitialState is executed on every render. Even though the value returned from it is only used during the 1st render.

I know this is normal Javascript behavior. But is there a way to avoid it using React?

function App() {

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

  const [myState,setMyState] = React.useState(getInitialState());
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {JSON.stringify(myState)}</div>
      <div>My boolean: {JSON.stringify(myBoolean)}</div>
      <button onClick={()=>setMyBoolean((prevState) => !prevState)}>Force Update</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src=".8.3/umd/react.production.min.js"></script>
<script src=".8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

See form the snippet below that the getInitialState is executed on every render. Even though the value returned from it is only used during the 1st render.

I know this is normal Javascript behavior. But is there a way to avoid it using React?

function App() {

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

  const [myState,setMyState] = React.useState(getInitialState());
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {JSON.stringify(myState)}</div>
      <div>My boolean: {JSON.stringify(myBoolean)}</div>
      <button onClick={()=>setMyBoolean((prevState) => !prevState)}>Force Update</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"/>

Share Improve this question asked Sep 25, 2019 at 10:18 cbdevelopercbdeveloper 31.3k44 gold badges196 silver badges393 bronze badges 3
  • why do you need it to be a function rather a constant? – skyboyer Commented Sep 25, 2019 at 10:59
  • @skyboyer it's possible the asker is determining the state from an API response or a similar dynamic source. Then a function would become necessary. – ADTC Commented Oct 24, 2022 at 13:21
  • 1 @ADTC well, async call for a useState initialization will never work. So the only need for "avoid calculation of initial value on every render" is only "it's too heavy". – skyboyer Commented Oct 24, 2022 at 14:38
Add a comment  | 

4 Answers 4

Reset to default 9

Don't call the function directly inside the useState, pass an anonymous function to trigger the function. Refer to the official Doc.

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

  const [myState,setMyState] = React.useState(()=>getInitialState());
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {JSON.stringify(myState)}</div>
      <div>My boolean: {JSON.stringify(myBoolean)}</div>
      <button onClick={()=>setMyBoolean((prevState) => !prevState)}>Force Update</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"/>

You need to compute your state inside a function as per docs:

const [myState,setMyState] = useState(() => {
  const initialState = getInitialState();
  return initialState;
});

Or shorter:

const [myState, setMyState] = useState(getInitialState);

Yet another hook gotcha!

Try memoization with React.useMemo

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

function App() {
  const getInitial = React.useMemo(() => getInitialState(), []);
  const [myState, setMyState] = React.useState(getInitial);
  const [myBoolean, setMyBoolean] = React.useState(false);

  return (
    <React.Fragment>
      <div> I am App </div>
      <div> My state: {JSON.stringify(myState)} </div>
      <div> My boolean: {JSON.stringify(myBoolean)} </div>
      <button onClick={() => setMyBoolean(prevState => !prevState)}>
        Force Update
      </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" />

All good solutions, my two cents = Dominic's shorter solution above. :)

You're passing a computed function (the result of the function is passed to useState) If you pass your function, React knows that it needs to call that function only the first time:

function App() {

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

  const [myState,setMyState] = React.useState(getInitialState); // <-- pass the function, not the call to the function (result)
  const [myBoolean,setMyBoolean] = React.useState(false);
  
  return(
    <React.Fragment>
      <div>I am App</div>
      <div>My state: {JSON.stringify(myState)}</div>
      <div>My boolean: {JSON.stringify(myBoolean)}</div>
      <button onClick={()=>setMyBoolean((prevState) => !prevState)}>Force Update</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"/>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论