React + useState() + previousState
Asked Answered
E

3

7

I am quite new to react and have problems in understanding the useState Hook - or more specifically the aspect of the previousState.

A normal useState Hook, and probably most common example, looks like this:

import React, { useState} from 'react';

export default function CounterHooks({ initialCount }){
  const [count, setCount] = useState(initialCount);

  return (
    <div>
      <button onClick={() => setCount(count -1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  )
}

What I am understanding so far, is the following:

  • I call the useState() function/Hook? and pass in the argument of the initial State (initialCount)
  • I get back an array which i immediately destruct into the variables count and setCount (which is function)
  • With setCount() i can update the state and therefore the count variable

So far so good, i think... ;)

Sometimes I see the same counter example with prevState (prevCount) which i do not understand:

<button onClick={() => setCount(prevCount => prevCount -1)}>-</button>

What happens here? This part i do not understand. My thoughts so far:

  • In this case, I somehow access the previous count value.
  • setCount expects now a function
  • setCount is now run asynchron
  • Where is this function coming from?
  • Where is prevCount coming from?
  • When i run this, what is put into prevCount?

Do you understand my confusion? I am not sure how i should frame this differently...

Thank you very much for your help.

Engelhardt answered 11/9, 2020 at 18:49 Comment(3)
Everything you said is right. The state setter function can receive a function instead of a value. If the setter receives a function it will call that function to set the state with the functions return value. Also, it will pass the previousState as a parameter to that function. This is how you can change the state based on the previous state. Please find a more detailed answer here: #56405319Horsa
Hi Andre, thank you for your help. I read this in the documentation and i did not understand it: reactjs.org/docs/hooks-reference.html#functional-updates. What I am not understanding is: what function do I pass into setState()/setCount().Engelhardt
"What I am not understanding is: what function do I pass into setState()/setCount(). " A function that accepts a single argument (the previous value) and returns the new value (which might or might not be based on the previous value). Maybe the documentation about setState makes it clearer (towards the end): reactjs.org/docs/react-component.html#setstate . If the new state does not depend on the previous state then there is no need to pass a function.Trainband
L
11

First of all, you can see the official explanation here.

In this case, I somehow access the previous count value.

You are not somehow accessing the previous count value. If you use a functional update and give a callback function to the setter, it returns the previous state to you and you use this value in your callback function.

setCount expects now a function

Since you provide a function, it can use it.

setCount is now run asynchron

Actually, no. It is not an asynchronous function. It just provides the previous state and you use it. State setters are not asynchronous, state update is.

Where is this function coming from?

Where is prevCount coming from?

Already answered.

When i run this, what is put into prevCount?

You provide what do you want to be set the new state there. In your example, you want to increment count by 1, so you are providing + 1

Here is a naive explanation for this logic. Just a naive one. I'm adding this to exemplify the callback logic, this is not related to React's setState.

let state = 5;

function setState(val) {
  if (typeof val === "function") {
    return val(state);
  }

  return val;
}

const stateWithFunctionalUpdate = setState(prev => prev + 1);
const stateWithNormalUpdate = setState(9);
console.log(stateWithFunctionalUpdate);
console.log(stateWithNormalUpdate);

Maybe this example would be suitable for mimicking the React's state setting logic. Again, just a naive approach.

let state = 5;

function setState(val) {
  if (typeof val === "function") {
    state = val(state);
  } else {
    state = val;
  }

}

setState(9);
setState(prev => prev + 1);

console.log("state", state);

Let's look at the real implementation of useState (without typings):

export function useState(initialState) {
  return useReducer(
    basicStateReducer,
    // useReducer has a special case to support lazy useState initializers
    initialState
  );
}

Well, it just uses useReducer behind the curtains. It sets the initial state and uses basicStateReducer which is:

function basicStateReducer(state, action) {
  return typeof action === "function" ? action(state) : action;
}

Like our naive approach, it looks for the type of the action and behaves according to that. Finally, this is what useReducer returns:

return [workInProgressHook.memoizedState, dispatch];

So, at the end of the day, if we provide a function to useState, it takes it and then run it on our state and returns the value back.

Long answered 11/9, 2020 at 19:15 Comment(2)
Hi devserkan, thank you very much for your detailed response. This is very helpful. I think i got it now. I pass a callback function to useState. useState calls the function. The callback function expects an argument, which setCount provides with the value of the variable count.Engelhardt
You are welcome. Well, you are almost right. You are not passing the callback to useState but rather to setState, setCounter or whatever the setter is :)Long
T
2

Here is my opinion how i understand:

In this case, I somehow access the previous count value. -> setCount will check param, if the param is a callback it will call that call back and pass previous state for param. Result of call back will be next state.

setCount expects now a function -> yes, function handle you logic and return next state.

setCount is now run asynchron -> this function is synchron but after it return result. The hook for setCount will dispatch event to update the next state

Where is this function coming from? -> your can do anything in function as long as param will be current state and return a next state.

Where is prevCount coming from? -> will be passed when call setCount(it's get from current state).

When i run this, what is put into prevCount? -> current state of setCount.

=> Example for how to pass a value or a call back to setCount here: useState hook, setState function. Accessing previous state value

Turbid answered 11/9, 2020 at 19:10 Comment(0)
P
0

There are two ways of using state in React.

const [data, setData] = useState();

Callback function with previous value:

setData((prevData)=> prevData + 1);

Use this approach when you think that value, object, or array that you want to set should be attached, merged or some how it is linked with the previous data then use this.

Fixed Value:

setData(false);

Use this when you don't need to consider the previous state of the data.

Parkway answered 5/3 at 9:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.