Why is my React useState updating my context asynchronously?

Viewed 730

I have a component that creates three radio buttons. Cicking one should update a context store I have elsewhere.

My state looks like this:

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

I store my state like this:

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

And then my component fires the functions onChange:

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

When I click a button the function fires, but the console.log shows the previous state, not the newly updated one. Similarly, my context also get updated one step behind.

What's going on here?

2 Answers

state update is asynchronous with useState hooks updater. You can read more about it on this post:

useState set method not reflecting change immediately

However you can solve the context value update like

return (
    <RadioButtonGroup
        onChange={(event) => {
            setIsChecked({ checked: event.target.value });
            const newStyle = { ...styling,  font: { ...styling.font, align: event.target.value } }
            setStyle(newStyle);
            myContext.setStyles(newStyle);
        }}
    />
)

or you can update the context value with useEffect hook like

useEffect(() => {
     myContext.setStyles(styling);
}, [styling]);

return (
    <RadioButtonGroup
        onChange={(event) => {
            setIsChecked({ checked: event.target.value });
            setStyle({ ...styling,  font: { ...styling.font, align: event.target.value } });
        }}
    />
)

Answer from Shubham Khatri should work as expected, but the cleanest solution is to use useEffect() hook:

useEffect(() => {
    // this is the place to update your context, and it will work every time style is updated, everytime, everywhere.
}, [style]);

This will prevent you from updating your context by hand whenever a change occurs.

Related