0

I have a menu handling component in React.

It receives props.choices as an object (which is turn passed as props from another parent component which is in turn rendered from its parent App.js following a useState call), along with a setstate function setChoices, similarly inherited as props.

After setChoices is called, the props object appears to update correctly using the component view developer tools BUT the console log still shows the original default initialized value. This is problematic because I'd like to check that the first and second choices are not the same.

The rendered values in the return JSX update correctly, and furthermore other components that use the same state as props are updating correctly. Component view in developer tools also shows that the state is being correctly updated.

The problem I have therefore appears to be accessing the newly updated state (which would allow me to compare the first and second choices).

Thanks for suggestions - this is driving me only slightly mad.

import React from 'react';

export function ConfirmChoices(props) {

    // destructure props
    const {
        selectedMenuOption,
        selectedMenuSubOption,
        choices,
        setChoices
    } = props;


    //TODO: choice handling, including updating choices state in App.js
    function handleSetChoicesClick(event) {

        if (event.target.id === 'firstChoiceButton') {
            setChoices({...choices, first: selectedMenuSubOption, firstOption: selectedMenuOption});

            // THIS DOES NOT RETURN THE CORRECT VALUE - BUT IT DOES RENDER CORRECTLY IN THE RETURN FUNCTION <P> BELOW
            console.log(choices.first);
        }

        if (event.target.id === 'secondChoiceButton') {
            setChoices({...choices, second: selectedMenuSubOption, secondOption: selectedMenuOption});
        }


    }

    // do not render anything if a suboption has not been selected
    if (selectedMenuSubOption === '') {
        return null;
    }

    return (
        <div>
                        <button id="firstChoiceButton"
                                onClick={handleSetChoicesClick}
                        >Set as first choice</button>
                        <button id="secondChoiceButton"
                                onClick={handleSetChoicesClick}
                        >Set as second choice</button>
                        <p>{choices.firstOption} - {choices.first}</p>
        </div>
    );
}

What I was expecting If the selected first choice option from the menu is 'Meeting' and suboption is 'Team', I would expect the console.log statement to result in 'Team', instead it returns the default value when the state was initialized in the parent component with useState ('not yet selected').

The

block in the returned JSX expression does indeed render correctly and results in 'Meeting - Team'

What I would like to be able to do Check the suboptions for equality (choices.first and choices.second) after the new state has been set by handleSetChoicesClick and blank out the other option to avoid duplicate first and second choices using something like this:

// If first choice button selected, immediately after console.log statement in previous code

            if (choices.first === choices.second) {
                setChoices({...choices, second: '', secondOption: ''});
            }

The solution State updates are only reflected in the next re-render of the component, which means trying to use them for logical operations before this can be problematic. The solution is to add a useEffect hook with a dependency array which includes the state variable.

See this answer here: The useState set method is not reflecting a change immediately

Here is my (now working) code:

import React, { useEffect } from 'react';

export function ConfirmChoices(props) {

    // destructure props
    const {
        selectedMenuOption,
        selectedMenuSubOption,
        choices,
        setChoices
    } = props;

    let lastClicked = '';


    useEffect(() => {


        console.log(choices.first);
        console.log(choices.second);

        if (choices.first === choices.second && choices.first !== '(Not yet selected)') {
            
            console.log(lastClicked);
            lastClicked === 'firstChoiceButton' ? setChoices({...choices, second: '', secondOption: ''}) : setChoices({...choices, first: '', firstOption: ''}); 

        }

    }, [choices]);


    //TODO: choice handling, including updating choices state in App.js
    function handleSetChoicesClick(event) {

        lastClicked = event.target.id;

        if (event.target.id === 'firstChoiceButton') {
            setChoices({...choices, first: selectedMenuSubOption, firstOption: selectedMenuOption});
        }
        
        if (event.target.id === 'secondChoiceButton') {
            setChoices({...choices, second: selectedMenuSubOption, secondOption: selectedMenuOption});
        }

        console.log(lastClicked);
    }


    // do not render anything if a suboption has not been selected
    if (selectedMenuSubOption === '') {
        return null;
    }

    return (
        <div>
                        <button id="firstChoiceButton"
                                onClick={handleSetChoicesClick}
                        >Set as first choice</button>
                        <button id="secondChoiceButton"
                                onClick={handleSetChoicesClick}
                        >Set as second choice</button>
        </div>
    );
}
  • 1
    Does this answer your question? [The useState set method is not reflecting a change immediately](https://stackoverflow.com/questions/54069253/the-usestate-set-method-is-not-reflecting-a-change-immediately) – David Jan 09 '23 at 21:01

0 Answers0