When to use useState initial value as function?
Asked Answered
G

4

73

What is the case where you use useState's initial value as a function?
Is there any difference from just passing a value?

e.g.

const [state, setState] = useState(() => someValue)
Grapevine answered 7/2, 2020 at 19:57 Comment(0)
I
131

You use it when you want the computation of that initial state to happen only once.

Because if you use an expression instead say:

const [state, setState] = useState(compute());

React saves the initial result of compute() once and ignores it on the next renders. But the compute function is still called on each render. This can be wasteful.

If you pass a function instead, the function is invoked only once.

From the docs:

const [state, setState] = useState(initialState);

The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render

const [state, setState] = useState(() => {
      const initialState = someExpensiveComputation(props);
      return initialState;
});

Also, in case you pass function as the argument to useState like the question is asking, it must satisfy certain requirements:

If you pass a function as initialState, it will be treated as an initializer function. It should be pure, should take no arguments, and should return a value of any type. React will call your initializer function when initializing the component, and store its return value as the initial state.

Innovate answered 7/2, 2020 at 20:1 Comment(3)
Why can't we do [state, setState] = useState(compute) ? Does it cause any issues? @giorgi-moniavaIncorporating
@Incorporating You can use that too. I updated the answer also with some info on the requirements on the initializer function.Innovate
Yes, B45i, useState(compute) vs useState(compute()) are different. The latter is executing the function on each render (e.g. wasteful) the other is passing a function to be executed once by setState (e.g. correct way to do it when you need to compute something once) |Septempartite
E
12

If you want to use useState's initial value as a function, you need to use currying :

const [state, setState] = useState(() => () => someValue);

This is because in the documentation, useState executes the provided function and considers its result as the initial value. Using currying, () => someValue is returned and considered to be the intial value.

Excitable answered 6/7, 2021 at 16:27 Comment(0)
E
4

The reason why wrapping in a function is less computationally heavy is because react will rerun the component function when props change. If useState() has a computation inside like useState(heavyComputation()), JavaScript will run heavyComputation(), but in useState(()=>heavyComputation()), JavaScript will not run heavyComputation(), but will pass in the curried function, and useState knows not to rerun this function.

Normal

Render 1: useState(heavyComputation()) //JavaScript calls heavyComputation

Render 2: useState(heavyComputation()) //JavaScript calls heavyComputation

Render 3: useState(heavyComputation()) //JavaScript calls heavyComputation

...

Wrapped in Function (curried)

Render 1: useState(()=>heavyComputation()) //useState() calls heavyComputation

Render 2: useState(()=>heavyComputation()) //useState() uses the value from the previous render so heavyComputation() is not called

Render 3: useState(()=>heavyComputation()) //useState() uses the value from the previous render so heavyComputation() is not called

...

Epode answered 8/1, 2023 at 15:54 Comment(0)
C
0

When we have some heavy computation to initialize the state, we should use the function. And this is lazy initialization of the state. Here is a well-written blog on react state lazy initialization by kentcdodds.

Choragus answered 7/12, 2022 at 10:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.