Most examples of React hook’s lazy initial state uses inline arrow functions to showcase the usage:
function App() {
const [state, setState] = React.useState(() => expensiveComputation());
// ...
}
Paired with the idea that whatever you pass to React.useState
is the initial
value of the state, it can be hard to grasp the difference from the example
below:
function App() {
const [state, setState] = React.useState(expensiveComputation());
// ...
}
For me, it helps to visualize the difference if you assign what’s inside the parentheses to a constant.
function App() {
const initialState = 0;
const [state, setState] = React.useState(initialState);
// ...
}
Everytime App
re-renders, the function App
will re-run completely. This
means 0
is set to initialState
in every render. Now let’s do the same with
the expensive computation example:
function App() {
const initialState = expensiveComputation();
const [state, setState] = React.useState(initialState);
// ...
}
It’s pretty clear now that the expensive function is called every time the
component renders. React.useState
is just ignoring its result in
subsequent renders. And that’s what you want to avoid when passing a function to
the hook.
React.useState
implementation detects if you’re passing a function and makes
sure to call it once for the component’s lifetime.
The tradeoff now is that you’re creating a new function for every render. That’s
acceptable if the computation takes longer or is more complex than instantiating
an inline function. If that’s not the case (for example, when setting a constant
like 0
in the first example), go with passing the value directly to
React.useState
.