HardState & Immutability🐛 Debug Challenge

useState with object — not merging previous state

Buggy Code — Can you spot the issue?

// Simulates useState with full replacement (not merge)
let formState = { name: '', email: '', age: 0 };

function setFormState(update) {
  // useState replaces, does NOT merge like class setState
  formState = typeof update === 'function' ? update(formState) : update;
}

// Bug: replacing entire state instead of merging
setFormState({ name: 'Alice' }); // email and age lost!
setFormState({ email: 'alice@example.com' }); // name lost again!

console.log(formState.name);
console.log(formState.email);
console.log(formState.age);

Fixed Code

let formState = { name: '', email: '', age: 0 };

function setFormState(update) {
  formState = typeof update === 'function' ? update(formState) : update;
}

// Fix: always spread previous state when updating a field
setFormState(prev => ({ ...prev, name: 'Alice' }));
setFormState(prev => ({ ...prev, email: 'alice@example.com' }));

console.log(formState.name);
console.log(formState.email);
console.log(formState.age);

Bug Explained

Bug: Unlike class setState, useState's setter REPLACES state. Setting { name: 'Alice' } loses email and age. The second update then loses name.

Explanation: Functional form with spread: each update preserves all previous fields and only overrides the targeted one.

Key Insight: Critical: useState replaces state entirely. Class setState merges. With object state in hooks, always spread: setForm(prev => ({ ...prev, field: value })).

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab