useState

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 2 min

useState

useState is the most fundamental React Hook. It lets you add state variables to functional components.

Syntax

const [state, setState] = useState(initialState);
  • initialState: The value you want the state to be initially. It can be a value of any type (number, string, boolean, object, array, etc.). It is ignored after the initial render.
  • state: The current state value.
  • setState: The state setter function that lets you update the state and trigger a re-render.

Lazy Initialization

If calculating the initial state is expensive (e.g., reading from localStorage or doing complex math), you can pass a function to useState. This function will only be executed once, during the initial render.

// ❌ BAD: readFileSync is called on EVERY render
const [data, setData] = useState(readFileSync('/file.txt'));

// ✅ GOOD: The arrow function ensures readFileSync is only called on the FIRST render
const [data, setData] = useState(() => readFileSync('/file.txt'));

Updating Objects and Arrays in State

State in React should be treated as immutable. When your state is an object or an array, you must not mutate it directly. Instead, you must create a new object or array and pass it to the state setter.

const [user, setUser] = useState({ name: 'Alice', age: 25 });

// ❌ WRONG: Mutating the existing object
function badUpdate() {
  user.age = 26;
  setUser(user); // React won't re-render!
}

// ✅ CORRECT: Creating a new object using the spread operator
function goodUpdate() {
  setUser({ ...user, age: 26 });
}

Similarly, for arrays, avoid mutating methods like push, pop, splice, or sort. Use non-mutating methods like concat, filter, map, or the spread syntax instead.

const [items, setItems] = useState(['Apple', 'Banana']);

// ❌ WRONG
items.push('Orange');
setItems(items);

// ✅ CORRECT
setItems([...items, 'Orange']);

Interview Questions

Q: Why shouldn’t you directly mutate state (e.g., state.count = 1)?
A: Because React determines if it needs to re-render the UI by checking if the state reference has changed (Object.is). If you mutate the object directly, the reference remains exactly the same, and React won’t trigger a re-render, leaving your UI completely out of sync with your data. Always use the setter function.

Q: How does the functional update form of useState work, and why use it?
A: The functional form (e.g., setCount(prev => prev + 1)) guarantees you are calculating the next state based on the most absolutely up-to-date previous state. If you rely on the closure variable (setCount(count + 1)) during rapid consecutive updates, you may encounter stale closures and lose state updates.