'useState not updating displays on first click

I have a table, whose rows can be clicked. Once clicked, they should load an objects values in to some inputs .value fields. It does do this, but only once the same table row has been clicked twice.

Here's some images explaining it:

Page is loaded: Image showing the page when loaded

Then, after clicking the middle record: Image showing the error

Then, after clicking the same record again: Image showing the correct information, only after the second mouse click

I use a useState to track all of the characters key:value pairs and another to track all of the display fields. But I'm not sure how to stop this error from happening.

Here's the code for my onClick function, attached to each table row:

function barkCharData(idIn){

    //set the st_charData state to contain whatever character was selected
    set_st_charData({
        id: ar_charBin[idIn-1].id,
        fName: ar_charBin[idIn-1].first_name,
        sName: ar_charBin[idIn-1].second_name,
        age: ar_charBin[idIn-1].age,
        gender: ar_charBin[idIn-1].gender,
        race: ar_charBin[idIn-1].race,
        classType:ar_charBin[idIn-1].classType,
        level: ar_charBin[idIn-1].level
    })

    console.log("After init, st_charData.id = "+st_charData.id)

    //then set st_boxes to be referenced
    set_st_displayBoxes({
        box_id: document.getElementById("txt_idField"),
        box_fName: document.getElementById("txt_fNameField"),
        box_sName: document.getElementById("txt_sNameField"),
        box_age: document.getElementById("txt_ageField"),
        box_gender: document.getElementById("txt_genderField"),
        box_race: document.getElementById("txt_raceField"),
        box_class: document.getElementById("txt_classField"),
        box_level: document.getElementById("txt_levelField"),
    })

    try{
        //From usestates, updated above,
        //assign the char data to the relevant box
        st_displayBoxes.box_id.value = st_charData.id
        st_displayBoxes.box_fName.value = st_charData.fName;
        st_displayBoxes.box_sName.value = st_charData.sName;
        st_displayBoxes.box_age.value = st_charData.age;
        st_displayBoxes.box_gender.value = st_charData.gender;
        st_displayBoxes.box_race.value = st_charData.race;
        st_displayBoxes.box_class.value = st_charData.classType;
        st_displayBoxes.box_level.value = st_charData.level;
     }
    
    catch(e)
    {
        console.log("\n\nPants were shat in:\n\t'barkCharData' function:\n" +e)
    }                    
}

And here's the code for an onChange handler which is attached to each of the text boxes:

    {
        try{
            console.log("Fired the change event");
            
            //update the useState with the change
            set_st_charData({ ...st_charData, [e.target.name]: [e.target.value] });
        }
        catch(error){
            console.log("Bowel movement detected in 'displayChangeHandler': "+error);
        }
    }

I know that the st_charData useState is initially empty before it's updated which I think is what's causing my issue. Problem is, I don't know how to work around it. Have tried looking at other answers but haven't managed to solve it.

Thanks for looking!



Solution 1:[1]

Gave myself tunnel vision on this one. Turned out to be quite straightforward, thanks to @GabrielePetroli for mentioning useEffect.

You can use the useEffect hook to fire code every time there's a re-render. You can also have the useEffect track a particular useState and re-render whenever that particular one changes.

useEffect(() => 
    { 
        //code you want to run each re-render
        //...like updating displays
    }, [<useState>,<something else>]);

By adding in things you want to track as a dependency in the array above, you'll force this useEffect to fire whenever the value changes and updating it in the new render.

The solution to my problem was to perform all of my assignments (updating useState with a newly clicked character record) in my onClick function and then having my useEffect paste the relevant info in to the inputs during re-renders.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Basil Nagle