What's the difference between
1
const [state, setState] = useState(0)
setState(state+1)
2
const [state, setState] = useState(0)
setState(...prevState => prevState+1)
In the first option, based on the documentation:
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
In the second option, called functional update:
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.
So basically if you'd like to be sure your state will be updated based on the previous state, you need to use the second option.
Read further in the official documentation of useState.
I hope this clarifies!
Related
I am new to react Hooks. Am trying to make use of useState in my code. While I was using it I found a term "Lazy initial state"
https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
But I am not able to think of any use case where this lazy initialising of state will be useful.
Like say my DOM is rendering and it needs the state value, but my useState has not initialised it yet! And say if you have rendered the DOM and the someExpensiveComputation has finished, the DOM will re-render!
The value passed to the useState hook in the initial render is the initial state value, and gets disregarded in subsequent renders. This initial value can be the result of calling a function as in the following situation:
const Component = () => {
const [state, setState] = useState(getInitialHundredItems())
}
But note that getInitialHundredItems is unconditionally and needlessly called on each render cycle.
For use cases like this instead of just calling a function that returns a value you can pass a function which returns the initial state. This function will only be executed once (initial render) and not on each render like the above code will. See Lazy Initial State for details.
const Component = () =>{
const [state, setState] = useState(getInitialHundredItems)
}
I saw React codes generally use arrow function for setState(). However, for useState(), I see that they just put a number, eg useState(0) instead of arrow function, ie useState( () => 0 )
Are there any reasons why I would want to use an arrow function in useState ? Is it alright to use only arrow functions for ALL React hook regardless, including initialising the hook. Any gotcha ? Thanks very much.
For your simple case it does not matter. You are just making an extra function call which returns a value without any computation.
But in case your initial state is a result of an expensive computation you can use a function.
Using the below
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
instead of
const [state, setState] = useState(someExpensiveComputation(props));
matters greatly.
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.
So even when your value is disregarded you would be making that expensive computation. You can provide a function in your useState which will only run on the very first render, like in the second case above. [Link](The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded.)
As far as I know, the arrow functions in state setter permit use the previous state.
For example:
const [message, setMessage] = useState('hello ')
... # Receive data from a form or API, etc.
let name = 'Richard'
setMessage(old_state => old_state + name) # 'hello Richard'
This way, you can create counter or reuse some data of the previous state.
Use setState(new_vale) is totally correct for reset the value.
As per React official documentation, if a component new state is computed using the previous state, one can pass a function to setState. The function will receive the previous value, and return an updated value.
Consider the following code snippet example:
const [counter, setCounter] = useState(0);
const btnClick = useCallback(() => setCounter(counter+1), [counter]); //(1)
const btnClick = useCallback(() => setCounter(previous => previous+1), []);//(2)
btnClick is passed as a callback function for the 'onClick' event of a rendered button.
In (1), the btnClick function reference is updated each time the counter state variable is modified.
In (2), the btnClick function reference is created during the first render cycle and is cached for the rest of the component lifetime; at this level, the state update function will receive the previous state value and return an updated value.
At this level, I have 2 questions:
1- As a good practice, should I opt for (1) or (2)? And are there any advantages for using (1) instead of (2)?
2- Does (2) apply to both primitive and reference types? (check below, I switched the state from being an Integer to an Object)
const [counter, setCounter] = useState({cntr:1});
const btnClick = useCallback(() => setCounter({...counter,cntr:counter.cntr+1}), [counter]); //(1)
const btnClick = useCallback(() => setCounter(previous => return {...previous ,cntr:previous.cntr+1}), []);//(2)
Does "previous" in (2) still reference the previous state value or does it refer to a stale state that was cached during the component first render cycle?
Your help is appreciated.
So, it is a deep question, let's try to explain how it works.
useCallback just promise you that btnClick will not reinitialize. This does not related to useState and body of function at all.
In react community we do not have any good practices on this point. The difference is when you call setState react promise you that sometimes value will be updated.
For example:
setValue(1);
setValue(2);
setValue(3);
It does not mean react will update the state three times. React can combine it to one update and set just last value.
Pass function as a argument to setState
const myFunc = (oldValue) => ({})
useValue(myFunc)
it means that react sees you pass function then all useValue will be called immediately and after useValue(myFunc) will be called.
Developers need it to work with last data which is placed in the state.
Does "previous" in (2) still reference the previous state value or
does it refer to a stale state that was cached during the component
first render cycle?
So, as a result of the question previous in setState will be recent value from state.
What's the difference between
1
const [state, setState] = useState(0)
setState(state+1)
2
const [state, setState] = useState(0)
setState(...prevState => prevState+1)
In the first option, based on the documentation:
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
In the second option, called functional update:
If the new state is computed using the previous state, you can pass a function to setState. The function will receive the previous value, and return an updated value.
So basically if you'd like to be sure your state will be updated based on the previous state, you need to use the second option.
Read further in the official documentation of useState.
I hope this clarifies!
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)
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())
The compute runs on other renders too, just its value is ignored after first* render.
So if you do:
const [state, setState] = useState(() => compute())
Now, compute will run 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;
});
* Well if it is strict mode then it could be the value of first render gets ignored too due to double invoking the render method. But this is not important for this answer. Because the value would now be ignored after second render.
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.
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.
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
...