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
|
4 Answers
Reset to default 9Don'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"/>
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