Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hooks: How do I update state on a nested object with useState()?

I have a component that receives a prop that looks like this:

const styles = {
    font: {
        size: {
            value: '22',
            unit: 'px'
        },
        weight: 'bold',
        color: '#663300',
        family: 'arial',
        align: 'center'
    }
};

I'm trying to update the align property, but when I try to update the object, I wind up replacing the whole object with just the align property.

this is how I'm updating it:

const { ...styling } = styles;
const [style, setStyle] = useState(styling);

return (
        <RadioButtonGroup
            onChange={(event) => {
                setStyle({ ...style, font: { align: event.target.value } });
                console.log(style);
            }}
        />);

When I console.log style I just get {"font":{"align":"left"}} back. I expected to see the whole object with the updated value for align. I'm new to destructuring so what am I doing wrong here?

like image 590
codemon Avatar asked Jun 28 '19 07:06

codemon


People also ask

How do I update state on a nested object with useState?

To update nested properties in a state object in React: Pass a function to setState to get access to the current state object. Use the spread syntax (...) to create a shallow copy of the object and the nested properties. Override the properties you need to update.

How do I update nested state in React hooks?

The primary rule of React state is do not modify state directly. That includes objects held within the top-level state object, or objects held within them, etc. So to modify your nested object and have React work reliably with the result, you must copy each layer that you change. (Yes, really.

How do I update state with useState?

If you use the previous value to update state, you must pass a function that receives the previous value and returns the new value: const Message = () => { const [message, setMessage] = useState( '' ); return ( <div> <input type="text" value={message} placeholder="Enter some letters" onChange={e => { const val = e.


2 Answers

You need to use spread syntax to copy the font object properties too. Also while trying to update current state based on previous, use the callback pattern

<RadioButtonGroup
  onChange={(event) => { 
    setStyle(prevStyle => ({
        ...prevStyle,
        font: { ...prevStyle.font, align: event.target.value }
    }));
    console.log(style);
  }}
/>
like image 69
Shubham Khatri Avatar answered Sep 28 '22 10:09

Shubham Khatri


This is your mistake

setStyle({
    ...style,
    font: { align: event.target.value } // This code replace the font object
});

To preserve the all font object values, you can do like this

const onChange = (event) => {
    const s = {...style};
    s.font.align = event.target.value;
    setStyle(s);
}

Or

const onChange = (event) => {
    setStyle({ 
        ...style,
        font: {
            ...style.font, // Spread the font object to preserve all values
            align: event.target.value
        }
    });
}
like image 44
tolotra Avatar answered Sep 28 '22 12:09

tolotra