how to update nested state properties in react

  • Last Update :
  • Techknowledgy :

In order to set the state of a specific nested field, you have set the whole object. I did this by creating a variable, newState and spreading the contents of the current state into it first using the ES2015 spread operator. Then, I replaced the value of this.state.flag with the new value (since I set flag: value after I spread the current state into the object, the flag field in the current state is overridden). Then, I simply set the state of someProperty to my newState object. Of course this is abusing some core principles, as the state should be read-only, but since you are immediately discarding the old state and replacing it with new state, it is completely ok. If you have deeply nested state, consider if you can restructure the child objects to sit at the root. This makes the data easier to update.

This is our Splunktool team suggestion, we tried and its working fine ✌
// However, there is a nested state method, which allows to access nested state by name.// example :- 
state = {
   user: {
      name: {
         firstName: 'Kaushal',
         lastName: 'Shah'
      }
   }
};​
// To access state => 
state​
// To access user => 
state.user or state.nested('user')​
// To access name => 
state.user.nested('name')​
// To access firstName => 
state.user.nested('name').nested('firstName')

In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates.

var someProperty = {
   ...this.state.someProperty
}
someProperty.flag = true;
this.setState({
   someProperty
})

The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object

Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:

this.state = {
   someProperty: {
      someOtherProperty: {
         anotherProperty: {
            flag: true
         }..
      }
      ...
   }
   ...
}

To write it in one line

this.setState({
   someProperty: {
      ...this.state.someProperty,
      flag: false
   }
});

Suggestion : 2

Approach 2: We can pass the old nested object using the spread operator and then override the particular properties of the nested object. There are the following approaches to update nested state properties in ReactJS: Step to Run Application: Run the application using the following command from the root directory of the project: CS SubjectsMathematicsOperating SystemDBMSComputer NetworksComputer Organization and ArchitectureTheory of ComputationCompiler DesignDigital LogicSoftware Engineering

Creating React Application:

Step 1: Create a React application using the following command:

npx create - react - app foldername

Step 2: After creating your project folder i.e. foldername, move to it using the following command:

cd foldername

Project Structure: It will look like the following.

Approach 1: App.js

import React, { Component } from "react";
class App extends Component {
  
  // Nested object
  state = {
    name: 'kapil',
    address: {
      colony: 'vaishnav nagar',
      city: 'Jaipur',
      state: 'Rajasthan'
    }
  };
  
  handleUpdate = () => {
    // Creating a dummy object using spread operator
    var address = { ...this.state.address }
  
    // Updating the city
    address.city = 'Kota';
    this.setState({ address })
  }
  
  render() {
    return (
      <div style={{ margin: 200 }}>
        <h1>{this.state.name}</h1>
        <h1>{this.state.address.colony} {","}
          {this.state.address.city}{", "}
          {this.state.address.state}</h1>
        <button
          onClick={this.handleUpdate}
        >UpdateCity </button>
      </div>
    );
  }
}
  
export default App;
5._
npm start

Suggestion : 3

Pass a function to setState to get access to the current state object. To update nested properties in a state object in React: When the next state is computed using the previous state, pass a function to setState. We passed a function to setState, because the function is guaranteed to be invoked with the current (most up to date) state.

1._
Copied!import {useState} from 'react';

export default function App() {
  const initialState = {
    name: 'Alice',
    address: {
      country: 'Austria',
      coords: [1, 2],
    },
  };
  const [employee, setEmployee] = useState(initialState);

  const updateNestedProps = () => {
    setEmployee(current => {
      // 👇️ using spread syntax (...)

      return {
        ...current,
        address: {
          ...current.address,

          // 👇️ override value for nested country property
          country: 'Germany',
        },
      };
    });
  };

  return (
    <div>
      <button onClick={updateNestedProps}>Click</button>

      <h4>{JSON.stringify(employee, null, 4)}</h4>
    </div>
  );
}
2._
Copied!
   const employee = {
      name: 'Alice',
      address: {
         country: 'Austria',
         coords: [1, 2],
      },
   };

const newEmployee = {
   ...employee,
   address: {
      ...employee.address,

      // 👇️ override country property
      country: 'Germany',
   },
};

// 👇️ newEmployee.address.country is 'Germany' now
console.log(newEmployee);
3._
Copied!
   const updateNestedProps = () => {
      setEmployee(current => {
         // 👇️ using spread syntax (...)

         return {
            ...current,
            address: {
               ...current.address,

               // 👇️ override value for nested country property
               country: 'Germany',
            },
         };
      });
   };

Suggestion : 4

The easiest way to update a nested object stored in the Reactjs state is to use the spread operator. I will explain it with several examples. We have a demo Reactjs app that handles a simple personal information form. The form contains the person's information, including address and email address. The address is nested in the person object, and the state and zip are nested in the address object. The stateZip is a nested object residing in the address object. The representation is person -> address -> stateZip. This person object is stored in the state as const [person, setPerson] = useState(initialState)

1._
interface person {
   name: string
   age: string
   address: {
      street: string,
      city: string,
      stateZip: {
         state: string,
         zip: string
      }
   }
   contact: {
      email: string
   } []
}
2._
let initialState: person = {
   name: "Lance",
   age: "36",
   address: {
      street: "100 Some street",
      city: "Edmond",
      stateZip: {
         state: "Oklahoma",
         zip: "73034"
      }
   },
   contact: [{
      email: "abc@learnbestcoding.com"
   }, {
      email: "aab@learnbestcoding.com"
   }]
}
3._
//This doesn't work
const updatePersonName = (name: string) => {
   person.name = value
   setPerson(person)
}
5._
const updateAddress = (event: HTMLInputElement) => {
   const {
      name,
      value
   } = event
   setPerson((prevPerson) => {
      const newPerson = {
         ...prevPerson
      }
      if (name === "street" || name === "city") {
         newPerson.address[name] = value
      }
      return newPerson
   })
}
6._
const updateStateZip = (event: HTMLInputElement) => {
   const {
      name,
      value
   } = event
   setPerson((prevPerson) => {
      const newPerson = {
         ...prevPerson
      }
      if (name === "state" || name === "zip") {
         newPerson.address.stateZip[name] = value
      }
      return newPerson
   })
}

Suggestion : 5

There are two main ways to deal with the problem of updating a deeply nested state. First is flattening your state to avoid the problem altogether. The second is using immutable libraries that help with state manipulations. There are libraries out there that are designed to help with immutable updates. For example, here is how Immer can help to reduce our boilerplate. But why is immutability so important if it makes the trivial task of updating a state so complicated? Here are three main reasons from React documentation:

1._
const updateTodo = ({
   taskId,
   todoId,
   value
}) => {
   setProject({
      tasks: {
         ...state.tasks,
         [taskId]: {
            ...state.tasks[taskId],
            todos: {
               ...state.tasks[taskId].todos,
               [todoId]: {
                  value: value
               }
            }
         }
      }
   })
}
2._
project.tasks[taskId].todos[todoId].value = true
3._
const updateTodo = ({
   taskId,
   todoId,
   value
}) => {
   setState(produce(baseState, draftState => {
      draftState.tasks[taskId].todos[todoId].value = value
      return draftState
   }))
}

Suggestion : 6

State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy. In other words, you should treat any JavaScript object that you put into state as read-only. You can store any kind of JavaScript value in state.

1._
const [x, setX] = useState(0);
2._
setX(5);
3._
const [position, setPosition] = useState({
   x: 0,
   y: 0
});
5._
import { useState } from 'react';
export default function MovingDot() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  return (
    <div
      onPointerMove={e => {
        position.x = e.clientX;
        position.y = e.clientY;
      }}
      style={{
        position: 'relative',
        width: '100vw',
        height: '100vh',
      }}>
      <div style={{
        position: 'absolute',
        backgroundColor: 'red',
        borderRadius: '50%',
        transform: `translate(${position.x}px, ${position.y}px)`,
        left: -10,
        top: -10,
        width: 20,
        height: 20,
      }} />
    </div>
  );
}

6._
onPointerMove = {
   e => {
      position.x = e.clientX;
      position.y = e.clientY;
   }
}

Suggestion : 7

How to update nested state properties in React In order to setState for a nested object you can follow the below approach as I think setState doesn't handle nested updates. However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper package to update the state. Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:

I'm trying to organize my state by using nested property like this:

this.state = {
   someProperty: {
      flag: true
   }
}

But updating state like this,

this.setState({
   someProperty.flag: false
});

Hello @kartik,

var someProperty = {
   ...this.state.someProperty
}
someProperty.flag = true;
this.setState({
   someProperty
})

You could setState using spread operator at each level like

this.setState(prevState => ({
   ...prevState,
   someProperty: {
      ...prevState.someProperty,
      someOtherProperty: {
         ...prevState.someProperty.someOtherProperty,
         anotherProperty: {
            ...prevState.someProperty.someOtherProperty.anotherProperty,
            flag: false
         }
      }
   }
}))

Suggestion : 8

Pretty uncontroversial up to this point! But what if we have nested state? That is, we need to update a property in our state object one (or more) levels deep? In React, we can update state using state setter functions. In function components, a state setter is provided by the useState hook. Incrementing a count using a button would look like this: If this feels redundant, it’s because it is! One option we have is to create a change handler that handles any property of user. Note that the following example depends on the name attribute of our inputs to map perfectly to our user state object.

1._
import { useState } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);
  return (
    <button
      onClick={() => {
        setCount(count + 1);
      }}
    >
      Increment
    </button>
  );
}
2._
class MyComponent extends React.Component {
  constructor() {
    this.state = { count: 0 };
  }

  render() {
    return (
      <button
        onClick={() => {
          this.setState({ count: this.state.count + 1 });
        }}
      >
        Increment
      </button>
    );
  }
}
3._
function SignupForm() {
  const [user, setUser] = useState({ email: '', password: '' });
  return (
    <form>
      <label htmlFor="email">Email address</label>
      <input id="email" name="email" value={user.email} />
      <label htmlFor="password">Password</label>
      <input id="password" name="password" value={user.password} />
    </form>
  );
}
5._
<input
  id="email"
  name="email"
  value={user.email}
  onChange={(e) => {
    setUser({
      ...user,
      email: e.target.value,
    });
  }}
/>
6._
<input
  id="password"
  name="password"
  value={user.password}
  onChange={(e) => {
    setUser({
      ...user,
      password: e.target.value,
    });
  }}
/>

Suggestion : 9

When using setState with a nested object and attempting to update just certain attributes of the state object, an expected state may be obtained, as will be detailed below. Rather of updating a single attribute of the nested state object and expecting Right to merge the objects, just duplicate the original state object and change it before calling setState with the modified copy of the original state object. Build a new state from the original state and the spread operator then call setState again with the updated state.

1._
state = {
   fullName: "Bob Fisher",
   email: '[email protected]'
}
2._
this.state = {
   fullName: "Bob Fisher",
   email: '[email protected]'
}
3._
this.setState({
   fullName: "Bob Fisher",
   email: '[email protected]'
});
5._
var newState = React.addons.update(this.state, {
   userInfo: {
      email: {
         $set: "[email protected]"
      }
   }
});
this.setState(newState);
6._
const {
   userInfo: oldUserInfo
} = this.state;
const userInfo = {
   ...oldUserInfo,
   email: "[email protected]"
};
this.setState({
   userInfo
});