Updating Objects in State
Updating Objects in State
A student record contains a name and several marks. Correcting one mark should preserve the other information while giving React a new record to render. Changing a property on the existing object is not the right update contract.
Treat objects stored in state as read-only. To change one property, create a new object containing the unchanged properties and the replacement. Then pass that new object to the setter or return it from a functional updater.
Explain
Create a new student record when correcting a mark.
Trace
Copy the changed nested marks object as well as its parent.
Apply
Explain why direct mutation does not reliably request a new screen.
Object Updates: follow the relationship
Original record
The current snapshot contains the existing student and marks objects.
Create changed copies
Copy the student and marks objects, then replace the corrected field.
Request the new record
The setter stores the new value while the old snapshot stays intact.
Build the mental model
Object spread copies properties into a new object. Put the changed property after the spread so it overrides the old value. Spread is shallow: nested objects are still shared unless you explicitly copy every level along the changed path.
Preserve record meaning
Replacing a student object with only {maths: 45} loses its name and every other field. Spreading the previous record first states that these fields should remain. A functional updater is particularly helpful if several requests may depend on the latest pending version of the same record.
For an object with many nested paths, outline the changed path before writing spreads. Copy each container along that path and keep untouched branches shared. This is structural sharing: it preserves snapshots without unnecessarily duplicating every unrelated record in the application.
Read the complete React example
The example changes maths from 40 to 45 and keeps name unchanged. The practice stores maths and science inside a marks object, so it copies both the student record and the nested marks object before replacing science.
React · JavaScript with JSX
import React, { useState } from 'react';
export default function App() {
const [student, setStudent] = useState({ name: 'Asha', maths: 40 });
return <main><h1>{student.name}</h1><p>Maths: {student.maths}</p><button onClick={() => setStudent(previous => ({ ...previous, maths: 45 }))}>Correct maths</button></main>;
}Expected screen and behavior
Initial screen: Asha Maths: 40 Correct maths
After selecting Correct maths: the screen includes Maths: 45.Connect the source to the screen
React · main example
import React, { useState } from 'react';
export default function App() {
const [student, setStudent] = useState({ name: 'Asha', maths: 40 });
return <main><h1>{student.name}</h1><p>Maths: {student.maths}</p><button onClick={() => setStudent(previous => ({ ...previous, maths: 45 }))}>Correct maths</button></main>;
}React can compare the old and new state values by identity. Creating a new record provides a new state value and preserves the earlier snapshot. Mutating the old record breaks the assumption that previous renders retain their own unmodified data.
Avoid the common mistakes
A shallow top-level copy followed by next.marks.science = 38 still mutates a shared nested marks object. Copy the nested object too. Very deeply nested state is harder to update correctly; a flatter structure or reducer may make the domain easier to manage.
Practice before reading the solution
React · starting example
import React, { useState } from 'react';
export default function App() {
const [student, setStudent] = useState({ name: 'Asha', maths: 40 });
return <main><h1>{student.name}</h1><p>Maths: {student.maths}</p><button onClick={() => setStudent(previous => ({ ...previous, maths: 45 }))}>Correct maths</button></main>;
}Correct a nested science mark while preserving the student name and maths mark.
Predict the initial visible text and explain which values determine it. Then inspect the worked solution below. Change one relevant input in your local project and check whether the screen follows your prediction. A working screen should be checked for the intended meaning, not merely the absence of an error.
Worked practice solution
The solution’s two spreads follow the path from the outer record to marks. Only science changes. Use the visible name and maths value as checks that the update retained required data rather than replacing the record with an incomplete object.
React · JavaScript with JSX
import React, { useState } from 'react';
export default function App() {
const [student, setStudent] = useState({ name: 'Asha', marks: { maths: 40, science: 35 } });
function correct() {
setStudent(previous => ({ ...previous, marks: { ...previous.marks, science: 38 } }));
}
return <main><h1>{student.name}</h1><p>Maths: {student.marks.maths}</p><p>Science: {student.marks.science}</p><button onClick={correct}>Correct science</button></main>;
}Expected screen and behavior
Initial screen: Asha Maths: 40 Science: 35 Correct science
After selecting Correct science: the screen includes Science: 38.Check your reasoning
Choose the explanation that matches React and JavaScript behavior. Review the reasoning for each alternative before moving on.
Use the idea in the next lesson
Treat objects stored in state as read-only. To change one property, create a new object containing the unchanged properties and the replacement. Then pass that new object to the setter or return it from a functional updater.
Continue with Updating Arrays in State. Revisit Queueing State Updates when the relationship between inputs and the resulting screen needs another look.
Read the primary reference: React documentation on Updating Objects in State.
Reaching the recap and completing the quizzes are separate milestones.