Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React + useState() + previousState

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.

like image 418
Fabian Avatar asked Sep 11 '20 18:09

Fabian


People also ask

Can I initialize useState with a function?

With the useState hook, you can pass a function as an argument to initialize the state lazily. As discussed, the initial value is needed only once at the first render. There is no point in performing this heavy computation on the subsequent renders.

What is prevState in React Hooks?

prevState() is the same as the setState but the only difference between them is that if we want to change the state of a component based on the previous state of that component, we use this. setState() , which provides us the prevState . Let's check an example of a counter app.

How do I get past state in React Hooks?

While there's currently no React Hook that does this out of the box, you can manually retrieve either the previous state or props from within a functional component by leveraging the useRef , useState , usePrevious , and useEffect Hooks in React.


1 Answers

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.

like image 136
devserkan Avatar answered Oct 05 '22 17:10

devserkan