1

Considering the following declaration:

   const [stateObject, setObjectState] = useState({
      firstKey: '',
      secondKey: '',
    });

Are the following snippets both corrects ?

A)

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

B)

setObjectState({
  ...stateObject,
  secondKey: 'value',
}));

I am sure that A) is correct, but is it necessary ? B) seems ok, but as setObjectState is an asynchronous function, stateObject might not have the most recent value.

skyboyer
  • 22,209
  • 7
  • 57
  • 64
Stéphane Gerber
  • 1,388
  • 1
  • 17
  • 30

2 Answers2

0

One useful thing about case of A that I have found is that you can use this method to update state from child components while only passing down a single prop for setObjectState. For example, say you have parent component with state you would like to update from the child component.

Parent Component:

import React, {useState} from 'react';
import ChildComponent from './ChildComponent';

export const ParentComponent = () => {
  const [parentState, setParentState] = useState({
    otherValue: null,
    pressed: false,
  });
  return (
    <ChildComponent setParentState={setParentState} />
  )
}

Child Component:

import React from 'react';

export const ChildComponent = (props) => {
  const callback = () => {
    props.setParentState((prevState) => ({
      ...prevState
      pressed: true,
    }))
  }
  return (
    <button onClick={callback}>test button<button>
  )
}

When the button is pressed, you should expect to see that the state has been updated while also keeping its initial values. As for the difference between the two, there isn't much as they both accomplish the same thing.

ppak10
  • 2,103
  • 3
  • 13
  • 24
0

A will always give you the updated value. B could be correct but might not. Let me give an example:

const Example = props => {
    const [counter, setCounter] = useState(0);

    useEffect(() => {
        // 0 + 1
        // In this first case the passed value would be the same as using the callback.
        // This is because in this cycle nothing has updated counter before this point.
        setCounter(counter + 1);

        // 1 + 1
        // Thanks to the callback we can get the current value
        // which after the previous iexample is 1.
        setCounter(latest_value => latest_value + 1);

        // 0 + 1
        // In this case the value will be undesired as it is using the initial
        // counter value which was 0.
        setCounter(counter + 1);
    }, []);

    return null;
};

When the new value depends on the updated one use the callback, otherwise you can simply pass the new value.

const Example = props => {
    const [hero, setHero] = useState('Spiderman');

    useEffect(() => {
        // Fine to set the value directly as
        // the new value does not depend on the previous one.
        setHero('Batman');

        // Using the callback here is not necessary.
        setHero(previous_hero => 'Superman');
    }, []);

    return null;
};

Also in the example you are giving it would probably be better to use two different states:

const [firstKey, setFirstKey] = useState("");
const [secondKey, setSecondKey] = useState("");
Alvaro
  • 9,247
  • 8
  • 49
  • 76