ReactJS - prevState in the new useState React hook?
Asked Answered
R

8

92

I really like the new React hooks and I'm using them frequently for a project I'm working on. I'm coming across a situation where I want to use the prevState in the useState hook, but I'm not really certain on how to do this.

I've tried something like this, but it fails to compile.

const [ someState, setSomeState ] = useState( new Map() )
setSomeState( prevState.someState.set( key, value ) )

(by the way, this is to map an array of checkboxes to keep track of the ones that are check marked)

I'm trying to follow this example here, but without using the setState function.

Ravin answered 24/4, 2019 at 6:0 Comment(0)
C
29

In order to use Maps, you'll need to clone it before manipulating the values. Otherwise, it's mutating the original Map and React doesn't handle mutatable state.

const handleChange = useCallback(({ target: { name, checked } }) => {
  setCheckbox(prevState => {
    return new Map(prevState).set(name, checked);
  });
}, []);

Updated Working Example:

Edit Multi Checkbox Handler

Canna answered 24/4, 2019 at 9:9 Comment(1)
Updated answer to include a working Map example. While this works, it may be less performant than other alternatives (especially if the Map becomes large).Canna
P
167

For objects you can use the spread operator to use prevState within your setState call.

const [object, setObject] = useState({
  firstKey: '',
  secondKey: '',
});

setObject((prevState) => ({
  ...prevState,
  secondKey: 'value',
}));

// object = {
//   firstKey: '',
//   secondKey: 'value',
// }

The snippet below show an example of using prevState for setting the state of an object.

const {useState} = React;

const Example = ({title}) => {
  const initialState = {
    firstKey: 'empty',
    secondKey: 'empty',
    thirdKey: 'not empty',
  }
  const [object, setObject] = useState(initialState);
  
  const withPrevState = () => {
    setObject((prevState) => ({
      ...prevState,
      secondKey: 'not empty',
    }));
  }

  return (
    <div>
      <h5>Updates Second key to 'not empty'</h5>
      <p>First key: {object.firstKey}</p>
      <p>Second key: {object.secondKey}</p>
      <p>Third key: {object.thirdKey}</p>
      <button onClick={withPrevState}>
        Update with prevState
      </button>
      <button onClick={() => {setObject({secondKey: 'not empty'})}}>
        Update without prevState
      </button>
      <button onClick={() => {setObject(initialState)}}>
        Reset
      </button>
    </div>
  );
};

// Render it
ReactDOM.render(
  <Example />,
  document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Phocine answered 14/8, 2019 at 21:13 Comment(2)
I finally understood how to use this prevstate with this snippet. ThanksMarva
I believe this decreases performance because React thinks all state entries were set to something new. Additionally, you are cloning an array, which also can cause a dramatic decrease in performance for states with many entries.Interfere
C
29

In order to use Maps, you'll need to clone it before manipulating the values. Otherwise, it's mutating the original Map and React doesn't handle mutatable state.

const handleChange = useCallback(({ target: { name, checked } }) => {
  setCheckbox(prevState => {
    return new Map(prevState).set(name, checked);
  });
}, []);

Updated Working Example:

Edit Multi Checkbox Handler

Canna answered 24/4, 2019 at 9:9 Comment(1)
Updated answer to include a working Map example. While this works, it may be less performant than other alternatives (especially if the Map becomes large).Canna
D
18

state updater from useState provides a callback pattern which returns you the previous state which you can use to update the current state

const [ someState, setSomeState ] = useState( new Map() )
setSomeState(prevState => prevState.set( key, value ) )
Degrade answered 24/4, 2019 at 6:5 Comment(5)
Hey I've tried this and I'm getting this error Cannot read property 'set' of undefined. Is the useState hook limited?Ravin
you might be using prevState.someState.set( key, value ) instead of prevState.set( key, value ) Degrade
Reference for the state updater: reactjs.org/docs/hooks-reference.html#usestateObligate
Just to confirm: why using the state from the callback and not someState? To avoid messy concurrent updates? I just see this often but never the technical explanation that comes along.Valuate
@EricBurel you don't always need to use functional setState but sometimes it can be really useful. This post explains it a some detail for class component but the same applied to functional components too. In additional functional state updates with hooks can be really useful to avoid closure issues as well since you are guaranteed to be provided the latest stateDegrade
T
9

You have already the previous state in the destructed variable: someState

so you can do:

const [ someState, setSomeState ] = useState( new Map() )
setSomeState( someState.set( key, value ) )
Tetravalent answered 26/11, 2019 at 17:17 Comment(1)
Don't know about the OP but this solved a problem I was facing. ThankYou. And smart !Tempt
A
8

use this

const [someState, setSomeState] = useState({thing: 'loading', count: 1});
setSomeState(prev => ({...prev, count: prev.count + 1}));
Ailin answered 4/5, 2021 at 6:11 Comment(0)
H
4

If you want to update a single value using prevState and useState - You can simply follow this

const [count, setCount] = useState(0) 
 
if(your-condition) {
  setCount((prevState) => prevState + 1)
}
Hypno answered 17/10, 2022 at 7:22 Comment(0)
U
0
  const [object,setObject] = useState({firstPropertie:'first',
    secondPropertie:'second'})

  setObject(prevState =>
  return{...prevState,secondPropertie:'newValue'})
Upward answered 3/10, 2022 at 19:34 Comment(0)
T
0

Updating objects and arrays in state

You can put objects and arrays into state. In React, state is considered read-only, so you should replace it rather than mutate your existing objects. For example, if you have a form object in state, don't mutate it:

// 🚩 Don't mutate an object in state like this
form.firstName = 'Taylor';

// Instead, replace the whole object by creating a new one: 
// ✅ Replace state with a new object
setForm({
    ...form,
    fistName: 'Taylor'
});

Read updating objects in state and updating arrays in state to learn more.

Thaddeus answered 20/1 at 0:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.