Skip to content
Advertisement

ReactJs : Nested State Not getting Updated

I have below code :

import React,{useState} from 'react'
const iState ={
    Name : '',
    Email :'',
    Salary :0,
    Error:{
        EName:'*',
        EEmail:'*',
        ESalary:'*'
    }

}
function ReactForm() {
    
const [state, setstate] = useState(iState);
    function validationHandler(e)
    {        
        switch (e.target.name) {
            
            case 'txtName':
                console.log(e.target.value);
                if(e.target.value=='')
                {
                    console.log('inside condition')
                    setstate({...state.Error, EName:'Value Cannot be blank'})
                    console.log(state);
                }
                else
                {
                    console.log('inside else condition')
                    setstate({...state, EName:''})
                }
                setstate({...state, Name:e.target.value})
                break;
                case 'txtEmail':
                setstate({...state, Email:e.target.value})
                break;
                case 'txtSalary':
                setstate({...state, Salary:e.target.value})
                break;
            default:
                break;
        }
        
        console.log(state);
    }

    return (
        <div>
            Name : <input name="txtName" type="text" onChange={(e)=>validationHandler(e)}></input>
            <label> {state.Error.EName==undefined ? '*':state.Error.EName} </label>
            <br></br>            
            Email : <input name="txtEmail" type="text" onChange={(e)=>validationHandler(e)}></input>
            <br></br>            
            Salary : <input name="txtSalary" type="text" onChange={(e)=>validationHandler(e)}></input>
            <br></br>
            <button onClick={validationHandler}>Validate Us</button>
        </div>
    )
}

export default ReactForm

In this, iState has Error portion nested within –

const iState ={
        Name : '',
        Email :'',
        Salary :0,
        Error:{
            EName:'*',
            EEmail:'*',
            ESalary:'*'
        }    
    }

When I am trying to update the nested state of Error , its not getting updated –

if(e.target.value=='')
                    {
                        console.log('inside condition')
                        setstate({...state.Error, EName:'Value Cannot be blank'})
                        console.log(state);
                    }
                    else
                    {
                        console.log('inside else condition')
                        setstate({...state, EName:''})
                    }

I can see it entering within else block , but not updating the state. I also tried with – setstate({...state.Error, EName:''})

EDIT 1 :

if(e.target.value=='')
                {
                    console.log('inside condition')
                    setstate({ ...state, Error: { ...state.Error, Ename: 'Value Cannot be blank' }})
                    console.log(state);
                }
                else
                {
                    console.log('inside else condition')
                    setstate({ ...state, Error: { ...state.Error, Ename: 'Value Cannot be blank' }})
                }

Setting state changed –setstate({ ...state, Error: { ...state.Error, Ename: 'Value Cannot be blank' }})

Still state not getting updated –

enter image description here

Advertisement

Answer

Unlike this.setState in class components, state setter function returned by the useState hook does not merges the old state with the new state – you need to do it yourself. Not doing this will overwrite the existing state.

Currently you are overwriting the existing state. Correct way to update the state is shown below:

This

setstate({...state.Error, EName:'Value Cannot be blank'})

should be

setstate({ ...state, Error: { ...state.Error, Ename: 'Value Cannot be blank' } })

Explanation of why this works:

First you spread the top level state object in the newly created object passed to setState. After that you add Error key and its value is another object in which you spread state.Error. Finally, you add a Ename key in the nested object and set its value.

Above steps re-create a new object with the similar structure as the initial state object.

Similarly this

setstate({...state, EName:''})

should be

setstate({...state, Error: { ...state.Error, EName:'' } })

Edit

Please note that state is updated asynchronously and it is constant within a particular render of a component.

This means that logging the state immediately after calling the state setter function will log the old state. Component can only see the updated state after it has re-rendered.

To log the updated state, use the useEffect hook as shown below:

useEffect(() => {
  console.log(state);
}, [state]);

This will log the updated state because it will execute:

  • after the initial render
  • every time component re-renders as a result of state update.
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement