Skip to main content

React State (useState & Local State Basics) — Complete Theory Guide


1. Introduction

What is State?

State in React represents data that changes over time and directly influences what gets rendered on the UI.
  • It is mutable (changeable) data managed within a component
  • When state changes → React re-renders the component
With modern React, we primarily use the useState hook to manage local state in functional components.
const [state, setState] = useState(initialValue);

Why is State Important in React?

React is built around declarative UI:
UI = f(state)
That means:
  • UI automatically reflects the current state
  • You don’t manually update the DOM
Without state:
  • UI would be static
  • No interactivity (forms, buttons, toggles, etc.)

When and Why We Use State

Use state when:
  • Data changes over time
  • UI needs to respond to user actions
  • You need to store temporary UI data
Common use cases:
  • Form inputs
  • Toggle (show/hide)
  • Counters
  • API response data
  • UI states (loading, error, success)

2. Concepts / Internal Workings

2.1 Local State vs Global State

  • Local State
    • Managed inside a component
    • Not shared unless passed via props
  • Global State
    • Shared across components
    • Managed via Context, Redux, etc.

2.2 How useState Works Internally

React uses an internal mechanism called Hooks Dispatcher + Fiber Tree. Key ideas:
  • React stores state outside the component function
  • Each render:
    • React maps hooks by call order
    • Not by variable name
⚠️ This is why:
Hooks must always be called in the same order

2.3 State Update Process

  1. You call setState
  2. React schedules an update
  3. Component re-renders
  4. New state value is used
setCount(count + 1);
But internally:
  • React may batch updates
  • Updates are asynchronous

2.4 Functional Updates

When next state depends on previous state:
setCount(prev => prev + 1);
Why?
  • Prevents stale state issues
  • Works correctly with batching

2.5 Re-rendering Behavior

  • State change → triggers re-render
  • Entire component function runs again
  • React compares virtual DOM → updates only necessary parts

2.6 Relationship with Other React Features

With Props

  • State is internal
  • Props are external
  • State can be passed down as props
<Child count={count} />

With useEffect

  • State changes can trigger side effects
useEffect(() => {
  console.log(count);
}, [count]);

With Rendering

  • State directly controls UI output
{isVisible && <Modal />}

3. Syntax & Examples


3.1 Basic useState Example

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

3.2 Multiple State Variables

const [name, setName] = useState("");
const [age, setAge] = useState(0);

3.3 Object State

const [user, setUser] = useState({ name: "", age: 0 });

setUser({ ...user, name: "John" });
⚠️ Always spread previous state

3.4 Array State

const [items, setItems] = useState([]);

setItems([...items, "New Item"]);

3.5 Toggle Example

const [isOpen, setIsOpen] = useState(false);

<button onClick={() => setIsOpen(prev => !prev)}>
  Toggle
</button>

3.6 Controlled Input

const [input, setInput] = useState("");

<input
  value={input}
  onChange={(e) => setInput(e.target.value)}
/>

3.7 Lazy Initialization

const [value, setValue] = useState(() => {
  return expensiveCalculation();
});
  • Runs only once on initial render

3.8 Derived State (Avoid storing when possible)

❌ Bad:
const [fullName, setFullName] = useState("");
✅ Better:
const fullName = firstName + " " + lastName;

4. Edge Cases / Common Mistakes


4.1 State Updates Are Asynchronous

setCount(count + 1);
console.log(count); // Old value
Fix:
  • Use functional update or useEffect

4.2 Stale State Problem

setCount(count + 1);
setCount(count + 1); // Only increments once
Fix:
setCount(prev => prev + 1);
setCount(prev => prev + 1);

4.3 Mutating State Directly

❌ Wrong:
user.name = "John";
setUser(user);
✅ Correct:
setUser({ ...user, name: "John" });

4.4 Missing Dependencies in Effects

useEffect(() => {
  console.log(count);
}, []); // Bug
Fix:
}, [count]);

4.5 Too Much State

  • Storing unnecessary data
  • Leads to complexity and bugs

4.6 Using State for Derived Values

Avoid storing values you can compute

4.7 Infinite Re-renders

setCount(count + 1); // inside render
Fix:
  • Move into event or effect

5. Best Practices


5.1 Keep State Minimal

Store only:
  • What changes
  • What is necessary

5.2 Use Functional Updates When Needed

Always use when:
  • Next state depends on previous
setCount(prev => prev + 1);

5.3 Normalize State Shape

Avoid deeply nested objects: ❌ Bad:
user.profile.details.name
✅ Better:
userName

5.4 Split State Logically

Instead of:
const [state, setState] = useState({ a: 1, b: 2 });
Use:
const [a, setA] = useState(1);
const [b, setB] = useState(2);

5.5 Avoid Unnecessary Re-renders

  • Use memoization (React.memo, useMemo)
  • Avoid updating state if value hasn’t changed

5.6 Use Lazy Initialization for Expensive Work

useState(() => computeHeavy());

5.7 Naming Conventions

  • Use clear naming:
    • count / setCount
    • isOpen / setIsOpen

5.8 Keep State Close to Usage

  • Lift state only when necessary
  • Avoid over-sharing state

5.9 Prefer Derived Data Over Stored Data

const total = items.reduce(...);
Instead of storing total

5.10 Think in UI State, Not Variables

Ask:
“What UI behavior does this state represent?”

Final Summary

  • State is the core of interactivity in React
  • useState enables functional components to manage dynamic data
  • Understanding:
    • Re-rendering
    • Asynchronous updates
    • State immutability
    is critical for writing correct React code
Mastering state is the foundation for:
  • Hooks
  • Performance optimization
  • Scalable React architecture

Senior-Level Conceptual Questions — React State (useState & Local State)


1. Why does React rely on call order to track useState instead of variable names?

Answer: React identifies hooks using call order, not variable names, because:
  • Components re-run on every render → variables are re-created
  • React needs a stable mapping between renders
  • Call order is deterministic as long as rules are followed
Internally:
  • React stores hooks in a linked list (Fiber node)
  • Each useState call reads the next slot
const [a] = useState(1);
const [b] = useState(2);
If order changes → mapping breaks → bugs ❌ Bad:
if (condition) {
  useState(0); // breaks order
}
Why this design?
  • Faster than name-based tracking
  • Avoids runtime parsing or identifiers
Alternative?
  • Key-based hooks → slower, more complex

2. Why are state updates asynchronous and batched?

Answer: React batches updates for:
  1. Performance
    • Multiple updates → single re-render
  2. Consistency
    • Avoid intermediate UI states
setCount(c => c + 1);
setCount(c => c + 1);
→ Single render, final value = +2 Under the hood:
  • Updates go into a queue
  • React schedules rendering via Fiber
Trade-off:
  • You can’t rely on immediate state
setCount(count + 1);
console.log(count); // stale
Solution:
  • Functional updates
  • useEffect for post-update logic

3. What problem does the functional update form solve?

Answer: It solves stale closure issues.
setCount(prev => prev + 1);
Why needed:
  • Closures capture old values
  • Multiple updates may use outdated count
❌ Problem:
setCount(count + 1);
setCount(count + 1); // same value used twice
✅ Fix:
setCount(prev => prev + 1);
setCount(prev => prev + 1);
Key Insight:
  • Functional updates read from React’s internal queue, not closure

4. Why does mutating state directly fail to trigger re-renders?

Answer: React relies on reference comparison, not deep checks.
user.name = "John";
setUser(user); // same reference → no re-render
React checks:
oldState === newState
If true → skip render Correct approach:
setUser({ ...user, name: "John" });
Why this design?
  • Deep comparison is expensive
  • Immutability enables fast diffing

5. How does React decide whether to re-render a component after state change?

Answer: React always re-renders the component when state updates, but:
  • It diffs virtual DOM
  • Only updates changed parts
Optimization layers:
  • React.memo
  • useMemo
  • useCallback
Important nuance:
  • Re-render ≠ DOM update

6. What are the trade-offs of storing derived state?

Answer: Derived state = computed from other state ❌ Bad:
const [total, setTotal] = useState(0);
Problems:
  • Duplication
  • Sync bugs
  • Extra renders
✅ Better:
const total = items.reduce(...);
When is derived state okay?
  • Expensive computation
  • Memoized with useMemo

7. Why is useState not suitable for complex state logic?

Answer: useState works best for simple, isolated values Problems with complexity:
  • Multiple interdependent states
  • Hard to manage updates
  • Logic duplication
Alternative: 👉 useReducer
const [state, dispatch] = useReducer(reducer, initialState);
Why better?
  • Centralized logic
  • Predictable transitions

8. What happens internally when setState is called?

Answer:
  1. Create an update object
  2. Push into update queue
  3. Schedule render (Fiber scheduler)
  4. During render:
    • Apply queued updates
    • Compute new state
Important:
  • Not applied immediately
  • Happens in next render cycle

9. Why can calling setState in render cause infinite loops?

Answer: Render → setState → render → setState → loop
function App() {
  const [count, setCount] = useState(0);
  setCount(count + 1); // ❌
}
Fix:
  • Move to event or useEffect

10. How does React handle multiple useState calls in one component?

Answer: Each call:
  • Gets its own slot in hook list
  • Independent state
const [a, setA] = useState(1);
const [b, setB] = useState(2);
Why not one object?
  • Avoid unnecessary re-renders
  • Better separation of concerns

11. When should you lift state up vs keep it local?

Answer: Keep local if:
  • Used by one component
Lift up if:
  • Shared across components
Trade-off:
  • Lifting increases coupling
  • Local improves encapsulation

12. Why is lazy initialization useful in useState?

Answer:
const [value] = useState(() => expensive());
  • Runs only on first render
  • Avoids repeated computation
Why needed?
  • Component function runs on every render

13. What are stale closures and how do they affect state?

Answer: Closures capture old state values
setTimeout(() => {
  setCount(count + 1); // stale
}, 1000);
Fix:
setCount(prev => prev + 1);

14. Why should state be minimal and normalized?

Answer: Problems with large/nested state:
  • Hard updates
  • Bugs
  • Performance issues
Normalized state:
  • Flat structure
  • Easier updates

15. How does state affect component performance?

Answer: Each state update:
  • Triggers re-render
Performance issues:
  • Frequent updates
  • Large component trees
Solutions:
  • Split components
  • Memoization
  • Avoid unnecessary state

16. Why is it dangerous to rely on state immediately after setting it?

Answer: Because updates are queued
setCount(5);
console.log(count); // old value
Correct approach:
  • Use useEffect
  • Or functional updates

17. How do you prevent unnecessary state updates?

Answer: Check before updating:
if (count !== newValue) {
  setCount(newValue);
}
Or rely on React:
  • If same value → no re-render

18. What’s the difference between state and refs for storing values?

Answer:
FeatureStateRef
Causes re-render
Persistent value
UI-driven
Use refs for:
  • Mutable values
  • DOM access
  • Avoid re-renders

19. Why does React recommend splitting state instead of one big object?

Answer:
// ❌
const [state, setState] = useState({ a: 1, b: 2 });
Problems:
  • Partial updates tricky
  • Unnecessary re-renders
// ✅
const [a, setA] = useState(1);
Benefit:
  • Fine-grained updates

20. How does local state impact scalability in large applications?

Answer: Local state:
  • Great for encapsulation
  • Reduces global complexity
But:
  • Hard to share
  • Leads to prop drilling
Solution:
  • Combine with:
    • Context
    • State libraries

Final Insight

Senior engineers don’t just “use state” — they:
  • Understand how React schedules updates
  • Design minimal, predictable state
  • Avoid derived and duplicated data
  • Optimize for render performance and scalability

Senior-Level MCQs — React State (useState & Local State)


1. What will be logged?

const [count, setCount] = useState(0);

function handleClick() {
  setCount(count + 1);
  setCount(count + 1);
  console.log(count);
}
Options: A. 0 B. 1 C. 2 D. Depends on batching Correct Answer: A Explanation:
  • count inside the function is stale (0)
  • Both setCount(count + 1) use the same value → React batches → final state = 1
  • But console.log(count) runs before re-render, so logs 0
Why others are wrong:
  • B: Final state is 1, but not logged here
  • C: Requires functional updates
  • D: Batching doesn’t affect the logged value here

2. Which scenario guarantees correct increment behavior?

Options: A.
setCount(count + 1);
setCount(count + 1);
B.
setCount(prev => prev + 1);
setCount(prev => prev + 1);
C.
setTimeout(() => setCount(count + 1), 0);
D.
count += 2;
setCount(count);
Correct Answer: B Explanation:
  • Functional updates always use latest state from React queue
Why others are wrong:
  • A: Stale closure → only +1
  • C: Still captures stale count
  • D: Mutates variable, breaks React model

3. Why does this NOT re-render?

const [user, setUser] = useState({ name: "A" });

user.name = "B";
setUser(user);
Options: A. React ignores object updates B. State must be primitive C. Reference didn’t change D. setState is async Correct Answer: C Explanation:
  • React uses shallow comparison
  • Same object reference → no re-render
Why others are wrong:
  • A: React supports objects
  • B: State can be any type
  • D: Async is unrelated here

4. What happens if hooks are conditionally called?

if (flag) {
  useState(0);
}
Options: A. Works fine B. Only fails in strict mode C. Breaks hook order mapping D. Causes infinite loop Correct Answer: C Explanation:
  • React relies on consistent hook order
  • Conditional hooks break mapping → unpredictable state
Why others are wrong:
  • A: Not allowed
  • B: Always unsafe, not just strict mode
  • D: Not necessarily looping

5. What will this render?

const [count, setCount] = useState(0);

useEffect(() => {
  setCount(1);
}, []);

return <p>{count}</p>;
Options: A. 0 B. 1 C. First 0, then 1 D. Infinite loop Correct Answer: C Explanation:
  • Initial render → 0
  • Effect runs → updates to 1 → re-render
Why others are wrong:
  • A: misses update
  • B: ignores first render
  • D: dependency array prevents loop

6. Why is this problematic?

const [total, setTotal] = useState(items.length);
Options: A. items.length is expensive B. Derived state duplication C. State can’t store numbers D. Hooks must be async Correct Answer: B Explanation:
  • total can be derived from items
  • Risk of inconsistency
Why others are wrong:
  • A: trivial computation
  • C: numbers are valid
  • D: irrelevant

7. What will happen?

setCount(5);
setCount(5);
Options: A. Two renders B. One render C. No render D. Error Correct Answer: B Explanation:
  • React batches updates
  • Same value → only one render
Why others are wrong:
  • A: batching avoids this
  • C: state change still occurs
  • D: valid code

8. What is the issue here?

const [data, setData] = useState([]);

useEffect(() => {
  setData(fetchData());
}, [data]);
Options: A. Missing dependency B. Infinite loop C. fetchData must be async D. State cannot be array Correct Answer: B Explanation:
  • data changes → effect runs → updates data → loop
Why others are wrong:
  • A: dependency is present (problem is using it)
  • C: not required
  • D: arrays are valid

9. Which is the safest way to update nested state?

Options: A.
state.user.name = "X";
B.
setState({ user: { name: "X" } });
C.
setState(prev => ({
  ...prev,
  user: { ...prev.user, name: "X" }
}));
D.
setState(prev => prev.user.name = "X");
Correct Answer: C Explanation:
  • Preserves immutability at all levels
Why others are wrong:
  • A/D: mutation
  • B: overwrites entire state

10. What does lazy initialization prevent?

useState(() => compute());
Options: A. Re-render B. Re-computation on every render C. Memory leaks D. Async issues Correct Answer: B Explanation:
  • Function runs only once (initial render)

11. What happens here?

const [count, setCount] = useState(0);

setTimeout(() => {
  setCount(count + 1);
}, 1000);
Options: A. Always correct B. May use stale value C. Causes crash D. Infinite loop Correct Answer: B Explanation:
  • Closure captures old count

12. Which is true about state vs ref?

Options: A. Both trigger re-render B. Only ref triggers re-render C. Only state triggers re-render D. Neither triggers re-render Correct Answer: C

13. Why split state instead of one object?

Options: A. Required by React B. Better performance and isolation C. Objects are slower D. Hooks limitation Correct Answer: B

14. What happens if same state value is set?

setCount(1);
setCount(1);
Options: A. Two renders B. One render C. No render D. Crash Correct Answer: B

15. Why avoid setting state inside render?

Options: A. Performance only B. Infinite loop risk C. Not allowed syntax D. Only for class components Correct Answer: B

16. What is true about batching?

Options: A. Only in events B. Only in async code C. Happens automatically in React D. Not predictable Correct Answer: C

17. What is the main issue here?

const [value, setValue] = useState(props.value);
Options: A. Invalid syntax B. State won’t update with props C. Causes loop D. Too slow Correct Answer: B Explanation:
  • State initializes once
  • Does not sync with props automatically

18. What is the best alternative?

Options: A. Always use state B. Derive directly from props C. Use global state D. Use ref Correct Answer: B

Final Takeaway

These questions test whether you understand:
  • State is queued, not immediate
  • Closures + batching = tricky bugs
  • Immutability drives re-rendering
  • Design decisions matter more than syntax

React Coding Problems — State (useState & Local State Basics)

These problems simulate real-world product scenarios and are designed to test deep understanding of state behavior, edge cases, and design decisions.

1. Debounced Search Input

Problem

Build a search input that:
  • Updates UI immediately as user types
  • Calls API only after user stops typing for 500ms

Constraints

  • Avoid unnecessary API calls
  • Must handle rapid typing
  • No external debounce libraries

Expected Behavior

  • Typing “react” quickly → only 1 API call
  • UI still reflects typed value instantly

Edge Cases

  • Fast typing
  • Clearing input quickly
  • Component unmount before timeout

Solution Explanation

Key idea:
  • Store input state
  • Use timeout (debounce logic)
const [query, setQuery] = useState("");
const [debounced, setDebounced] = useState(query);

useEffect(() => {
  const id = setTimeout(() => {
    setDebounced(query);
  }, 500);

  return () => clearTimeout(id);
}, [query]);
WHY:
  • Separates UI state vs API state
  • Prevents excessive renders/API calls

2. Multi-Step Form with Validation

Problem

Create a 3-step form:
  • Each step has inputs
  • State persists across steps
  • Validation per step

Constraints

  • Cannot lose data on navigation
  • Must prevent moving forward on invalid input

Expected Behavior

  • Step navigation with preserved state
  • Validation errors shown per step

Edge Cases

  • Jumping back and forth
  • Partial completion
  • Reset form

Solution Explanation

Use centralized state:
const [form, setForm] = useState({
  name: "",
  email: "",
  password: ""
});
WHY:
  • Avoid splitting state across steps
  • Easier validation + persistence

3. Undo/Redo Feature

Problem

Implement undo/redo for a text editor.

Constraints

  • Maintain history
  • Efficient updates

Expected Behavior

  • Undo goes to previous state
  • Redo restores undone state

Edge Cases

  • Multiple undo levels
  • Undo after new change (clear redo stack)

Solution Explanation

const [history, setHistory] = useState([""]);
const [index, setIndex] = useState(0);

function update(value) {
  const newHistory = history.slice(0, index + 1);
  setHistory([...newHistory, value]);
  setIndex(prev => prev + 1);
}
WHY:
  • Avoid mutation
  • Maintain linear history

4. Toggle with Delayed Reset

Problem

Toggle a flag ON, but auto-reset after 3 seconds.

Constraints

  • Cancel previous timers on re-toggle

Expected Behavior

  • Clicking rapidly resets timer

Edge Cases

  • Multiple clicks
  • Unmount during timer

Solution Explanation

Use cleanup:
useEffect(() => {
  if (!isOn) return;

  const id = setTimeout(() => setIsOn(false), 3000);
  return () => clearTimeout(id);
}, [isOn]);
WHY:
  • Prevent stale timers

5. Derived State Trap

Problem

Show cart total from items list.

Constraint

  • Items can change dynamically

Expected Behavior

  • Total always accurate

Edge Case

  • Removing items

Solution Explanation

❌ Avoid:
const [total, setTotal] = useState(0);
✅ Use:
const total = items.reduce((sum, i) => sum + i.price, 0);
WHY:
  • Prevent inconsistency

6. Batched Updates Bug

Problem

Fix incorrect counter increment behavior.

Bug Code

setCount(count + 1);
setCount(count + 1);

Expected Behavior

+2 increment

Solution

setCount(prev => prev + 1);
setCount(prev => prev + 1);
WHY:
  • Avoid stale closure

7. Sync State with Props

Problem

Component receives value prop but allows editing locally.

Constraint

  • Must update when prop changes

Solution

const [local, setLocal] = useState(value);

useEffect(() => {
  setLocal(value);
}, [value]);
WHY:
  • Sync external changes

8. Dynamic Form Fields

Problem

Add/remove input fields dynamically.

Expected Behavior

  • Each field has its own state
  • Add/remove works seamlessly

Solution

const [fields, setFields] = useState([""]);

function add() {
  setFields([...fields, ""]);
}
WHY:
  • Array state management

9. Prevent Unnecessary Re-renders

Problem

Avoid re-render when setting same value.

Solution

if (value !== newValue) {
  setValue(newValue);
}

WHY

  • React may skip, but explicit check helps logic clarity

10. Loading State Race Condition

Problem

Multiple API calls → wrong loading state

Solution

Track request id:
const [id, setId] = useState(0);

useEffect(() => {
  const current = id;
  fetchData().then(() => {
    if (current === id) setLoading(false);
  });
}, [id]);
WHY:
  • Prevent stale responses

11. Controlled vs Uncontrolled Input Issue

Problem

Input loses value on re-render

Solution

<input value={value} onChange={e => setValue(e.target.value)} />
WHY:
  • Controlled state ensures consistency

12. Infinite Loop with State

Problem

useEffect(() => {
  setCount(count + 1);
}, [count]);

Solution

  • Fix logic or remove dependency
WHY:
  • State update triggers effect repeatedly

13. Optimistic UI Update

Problem

Update UI before API response

Solution

setItems(prev => [...prev, newItem]);

try {
  await api();
} catch {
  rollback();
}
WHY:
  • Improves UX

14. Toggle List Item Selection

Problem

Select/unselect items in list

Solution

setSelected(prev =>
  prev.includes(id)
    ? prev.filter(i => i !== id)
    : [...prev, id]
);

15. Persist State in LocalStorage

Problem

Keep state after refresh

Solution

const [value, setValue] = useState(() => {
  return localStorage.getItem("key") || "";
});

useEffect(() => {
  localStorage.setItem("key", value);
}, [value]);

16. Avoid State Explosion

Problem

Too many related states

Solution

Group logically OR use reducer WHY:
  • Maintainability

17. Modal State Management

Problem

Multiple modals with independent control

Solution

const [openModal, setOpenModal] = useState(null);
WHY:
  • Single source of truth

18. Form Reset After Submit

Problem

Reset form correctly

Solution

setForm(initialState);

Edge Case

  • Partial reset

19. Conditional Rendering Bug

Problem

{count && <Component />}

Issue

  • 0 hides component

Fix

{count > 0 && <Component />}

20. State Co-location vs Lifting

Problem

Where to place state?

Solution

  • Keep local if possible
  • Lift only when shared
WHY:
  • Avoid unnecessary complexity

Final Insight

These problems evaluate:
  • State correctness under async conditions
  • Understanding of React internals
  • Design trade-offs (local vs global)
  • Real-world bug handling

React Debugging Challenges — State (useState & Local State Basics)

These simulate real production bugs you’d catch in code reviews at senior level.

1. Stale Closure in Event Handler

Buggy Code

const [count, setCount] = useState(0);

function handleClick() {
  setTimeout(() => {
    setCount(count + 1);
  }, 1000);
}

What’s Wrong?

  • Uses stale count inside closure

WHY It Happens

  • setTimeout captures value at render time
  • React state updates don’t mutate existing closures

Fix

setTimeout(() => {
  setCount(prev => prev + 1);
}, 1000);

Best Practice

  • Always use functional updates for async logic

2. State Mutation Causing No Re-render

Buggy Code

const [user, setUser] = useState({ name: "A" });

function update() {
  user.name = "B";
  setUser(user);
}

What’s Wrong?

  • Direct mutation → no re-render

WHY

  • React compares reference
  • Same object → no update

Fix

setUser(prev => ({ ...prev, name: "B" }));

Best Practice

  • Treat state as immutable

3. Double Increment Bug

Buggy Code

setCount(count + 1);
setCount(count + 1);

What’s Wrong?

  • Only increments once

WHY

  • Both use same stale value
  • React batches updates

Fix

setCount(prev => prev + 1);
setCount(prev => prev + 1);

Best Practice

  • Use functional updates when depending on previous state

4. Infinite Re-render Loop

Buggy Code

useEffect(() => {
  setCount(count + 1);
}, [count]);

What’s Wrong?

  • Infinite loop

WHY

  • State update triggers effect → loop

Fix

useEffect(() => {
  setCount(prev => prev + 1);
}, []);

Best Practice

  • Carefully design dependencies

5. Derived State Desync

Buggy Code

const [items, setItems] = useState([]);
const [total, setTotal] = useState(0);

useEffect(() => {
  setTotal(items.length);
}, []);

What’s Wrong?

  • total not updated when items changes

WHY

  • Missing dependency

Fix

const total = items.length;

Best Practice

  • Avoid derived state

6. Uncontrolled → Controlled Warning

Buggy Code

const [value, setValue] = useState();

<input value={value} />

What’s Wrong?

  • Starts uncontrolled → becomes controlled

WHY

  • Initial undefined

Fix

const [value, setValue] = useState("");

Best Practice

  • Initialize controlled inputs properly

7. Lost State on Re-render

Buggy Code

const [count, setCount] = useState(0);

if (count > 5) {
  setCount(0);
}

What’s Wrong?

  • State update during render

WHY

  • Triggers infinite loop risk

Fix

useEffect(() => {
  if (count > 5) setCount(0);
}, [count]);

Best Practice

  • Never update state inside render

8. Incorrect State Sync with Props

Buggy Code

const [value, setValue] = useState(props.value);

What’s Wrong?

  • Doesn’t update when prop changes

WHY

  • useState only initializes once

Fix

useEffect(() => {
  setValue(props.value);
}, [props.value]);

Best Practice

  • Avoid copying props unless necessary

9. Race Condition in API Calls

Buggy Code

useEffect(() => {
  fetch(`/api?q=${query}`)
    .then(res => res.json())
    .then(setData);
}, [query]);

What’s Wrong?

  • Old responses overwrite new ones

WHY

  • Async order not guaranteed

Fix

useEffect(() => {
  let active = true;

  fetch(`/api?q=${query}`)
    .then(res => res.json())
    .then(data => {
      if (active) setData(data);
    });

  return () => { active = false };
}, [query]);

Best Practice

  • Handle async race conditions

10. Memory Leak with Timeout

Buggy Code

useEffect(() => {
  setTimeout(() => setShow(true), 2000);
}, []);

What’s Wrong?

  • Timeout not cleared

WHY

  • Runs after unmount

Fix

useEffect(() => {
  const id = setTimeout(() => setShow(true), 2000);
  return () => clearTimeout(id);
}, []);

Best Practice

  • Always cleanup side effects

11. Excessive Re-renders from State

Buggy Code

const [value, setValue] = useState("");

<input onChange={e => setValue(e.target.value)} />

What’s Wrong?

  • Re-render on every keystroke

WHY

  • State updates trigger render

Fix (if heavy UI)

const deferredValue = useDeferredValue(value);

Best Practice

  • Optimize frequent updates

12. Wrong Dependency Causing Loop

Buggy Code

useEffect(() => {
  setFiltered(list.filter(x => x.active));
}, [filtered]);

What’s Wrong?

  • Infinite loop

WHY

  • Effect depends on value it updates

Fix

useEffect(() => {
  setFiltered(list.filter(x => x.active));
}, [list]);

Best Practice

  • Dependencies should reflect inputs, not outputs

13. State Reset on Key Change

Buggy Code

<Component key={id} />

What’s Wrong?

  • State resets unexpectedly

WHY

  • Changing key remounts component

Fix

  • Avoid unnecessary key changes

Best Practice

  • Use stable keys

14. Overwriting State Instead of Merging

Buggy Code

setState({ name: "A" });

What’s Wrong?

  • Other fields lost

WHY

  • useState replaces, not merges

Fix

setState(prev => ({ ...prev, name: "A" }));

Best Practice

  • Always merge manually

15. Incorrect Toggle Logic

Buggy Code

setOpen(!open);

What’s Wrong?

  • Can break with async updates

WHY

  • Uses stale value

Fix

setOpen(prev => !prev);

Best Practice

  • Always use functional toggle

16. Expensive Initialization on Every Render

Buggy Code

const [data] = useState(expensive());

What’s Wrong?

  • Runs on every render

WHY

  • Function executed immediately

Fix

const [data] = useState(() => expensive());

Best Practice

  • Use lazy initialization

17. Improper List Update

Buggy Code

items.push(newItem);
setItems(items);

What’s Wrong?

  • Mutation

WHY

  • Same reference

Fix

setItems(prev => [...prev, newItem]);

Best Practice

  • Immutable updates

18. Conditional Hook Call

Buggy Code

if (visible) {
  useState(0);
}

What’s Wrong?

  • Breaks hook order

WHY

  • React relies on call order

Fix

  • Move hook outside condition

Best Practice

  • Follow Rules of Hooks strictly

Final Insight

These debugging scenarios reflect real production issues:
  • Closures + async = subtle bugs
  • Immutability = core to React rendering
  • Effects + dependencies = major failure point
  • State design = architecture decision, not just syntax

🧠 React Machine Coding Problems — State (useState & Local State Basics)

These are production-level, architect-grade problems designed to test:
  • State design
  • UI behavior modeling
  • Performance awareness
  • Real-world trade-offs

1. Google Docs-like Auto-Save Editor

Requirements

  • Text editor with live typing
  • Auto-save after 2 seconds of inactivity
  • Show status: Saving... / Saved

UI Behavior

  • Instant typing feedback
  • Save indicator updates correctly

State/Data Flow

  • content
  • saveStatus
  • debouncedContent

Edge Cases

  • Rapid typing
  • Network delay
  • Save failure

Performance

  • Avoid frequent API calls
  • Debounce updates

Architecture

  • Separate UI state vs persisted state
  • Use effect cleanup for debounce

Approach

  1. Track content
  2. Debounce using useEffect
  3. Trigger save when stable
  4. Update status based on promise

2. Advanced Multi-Select Dropdown (Tagging System)

Requirements

  • Select multiple options
  • Search filter
  • Keyboard navigation
  • Create new tag

UI Behavior

  • Selected tags displayed as chips
  • Dropdown filters in real-time

State

  • selectedItems
  • searchQuery
  • isOpen

Edge Cases

  • Duplicate tags
  • Fast typing
  • Blur vs click conflicts

Performance

  • Debounce search
  • Avoid re-rendering all options

Architecture

  • Controlled input
  • Derived filtered list

Approach

  • Maintain list immutably
  • Toggle selection logic

3. Infinite Scroll Feed (Instagram-like)

Requirements

  • Load posts as user scrolls
  • Show loader at bottom

State

  • posts
  • page
  • loading

Edge Cases

  • Duplicate fetch
  • Fast scrolling
  • API failure

Performance

  • Throttle scroll events
  • Avoid duplicate API calls

Architecture

  • Keep fetch logic isolated
  • Track request state

Approach

  • Increment page on scroll
  • Append data immutably

4. Undoable Form Builder

Requirements

  • Add/remove fields dynamically
  • Undo/redo changes

State

  • history
  • currentIndex

Edge Cases

  • Undo after new action resets redo
  • Large history memory

Performance

  • Avoid deep cloning unnecessarily

Architecture

  • Linear history stack

Approach

  • Slice history on update
  • Push new state

5. Real-Time Chat Input (Optimistic UI)

Requirements

  • Send message instantly
  • Show pending state
  • Replace with confirmed message

State

  • messages
  • pendingMessages

Edge Cases

  • Failed send
  • Duplicate messages

Performance

  • Minimal re-renders on updates

Architecture

  • Temporary IDs for optimistic updates

Approach

  • Add optimistic message
  • Replace on success

6. Dynamic Form with Conditional Fields

Requirements

  • Fields appear based on previous answers

State

  • formData
  • visibleFields

Edge Cases

  • Clearing parent removes child values

Architecture

  • Derived visibility logic

Approach

  • Compute visible fields from state

7. Drag & Drop List Reordering

Requirements

  • Reorder items via drag

State

  • items

Edge Cases

  • Drag cancel
  • Reordering same index

Performance

  • Avoid full list re-render

Approach

  • Swap indices immutably

8. Search with Result Caching

Requirements

  • Cache previous search results

State

  • query
  • cache

Edge Cases

  • Cache invalidation

Approach

if (cache[query]) return cache[query];

9. Modal Manager (Multiple Modals)

Requirements

  • Open/close multiple modals
  • Only one active

State

  • activeModal

Edge Cases

  • Rapid switching

Architecture

  • Central modal state

10. Notification System (Toast Queue)

Requirements

  • Queue notifications
  • Auto-dismiss

State

  • queue

Edge Cases

  • Multiple simultaneous notifications

Approach

  • FIFO queue

11. File Upload with Progress

Requirements

  • Show upload progress per file

State

  • files
  • progress

Edge Cases

  • Cancel upload
  • Retry

12. Tab System with Persistent State

Requirements

  • Each tab maintains its own state

State

  • activeTab
  • tabStates

Edge Cases

  • Switching tabs frequently

13. Shopping Cart with Quantity Sync

Requirements

  • Update quantities
  • Calculate totals

State

  • cartItems

Edge Cases

  • Negative quantity

14. Live Filter Dashboard

Requirements

  • Multiple filters
  • Combined filtering logic

State

  • filters

Edge Cases

  • Complex combinations

15. Form Draft Auto-Recovery

Requirements

  • Restore form after refresh

State

  • formData

Approach

  • Sync with localStorage

16. Pagination with Page Memory

Requirements

  • Remember last page visited

State

  • page

17. Expandable Tree View

Requirements

  • Expand/collapse nodes

State

  • expandedNodes

Edge Cases

  • Deep nesting

18. Theme Toggle with Persistence

Requirements

  • Dark/light mode
  • Persist preference

State

  • theme

19. Real-Time Typing Indicator

Requirements

  • Show “User is typing…”

State

  • isTyping

Edge Cases

  • Debounce typing

20. Collaborative Cursor Position

Requirements

  • Track cursor positions of users

State

  • cursors

Final Insight (Architect Level)

These problems test:
  • State modeling vs UI behavior
  • Avoiding derived state pitfalls
  • Handling async + race conditions
  • Performance via minimal updates
  • Scalability decisions (local vs shared state)

🧠 Senior Frontend Interview Questions — React State (useState & Local State)

Designed for FAANG-level interviews, these questions test:
  • Mental models
  • Trade-offs
  • Debugging instincts
  • Real-world decision making

1. When would you deliberately avoid using useState?

Follow-up

  • What alternatives would you consider and why?

Strong Answer

Avoid useState when:
  • Value doesn’t affect UI → use useRef
  • Derived data → compute instead
  • Complex transitions → useReducer
  • Shared state → Context/global store
Reasoning:
  • useState triggers re-render → unnecessary cost if UI unaffected

Weak Answer

“When state is complex, use something else.”
❌ Why it fails:
  • Vague, lacks reasoning about render impact

2. How does React internally associate state with a component instance?

Follow-up

  • What breaks this mechanism?

Strong Answer

  • React uses Fiber nodes + hook call order
  • Hooks stored in a linked list per component
  • Order must remain consistent across renders
Breaking case:
if (condition) useState();

Weak Answer

“React tracks state by variable name.”
❌ Incorrect mental model

3. Why is setState asynchronous, and what problems does that solve?

Follow-up

  • What problems does it introduce?

Strong Answer

Solves:
  • Batching updates → performance
  • Prevents intermediate inconsistent UI
Introduces:
  • Stale state bugs
  • Need for functional updates

Weak Answer

“Because React is async.”
❌ No reasoning

4. Explain a real bug caused by stale closures and how you’d debug it.

Strong Answer

Example:
setTimeout(() => {
  setCount(count + 1);
}, 1000);
Bug:
  • Uses old count
Debug:
  • Log values
  • Identify async boundary
Fix:
setCount(prev => prev + 1);

Weak Answer

“Use useEffect.”
❌ Doesn’t address root cause

5. How do you decide between multiple useState vs a single object state?

Follow-up

  • Performance implications?

Strong Answer

Split state when:
  • Independent updates
  • Avoid unnecessary re-renders
Single object when:
  • Strong coupling between fields
Trade-off:
  • Granularity vs complexity

Weak Answer

“Either works.”
❌ No architectural thinking

6. What are the dangers of storing derived state?

Follow-up

  • When is it acceptable?

Strong Answer

Problems:
  • Duplication
  • Sync bugs
Acceptable:
  • Expensive computations (with memoization)

Weak Answer

“It’s not recommended.”
❌ Missing reasoning

7. Why does mutating state sometimes “work” but is still dangerous?

Strong Answer

  • May appear to work if re-render triggered elsewhere
  • Breaks React’s referential equality model

Weak Answer

“React doesn’t allow mutation.”
❌ Oversimplified

8. How would you debug unnecessary re-renders caused by state?

Follow-up

  • Tools and strategies?

Strong Answer

  • Use React DevTools profiler
  • Check state updates frequency
  • Identify unnecessary updates
Fix:
  • Memoization
  • Split components
  • Avoid redundant state

Weak Answer

“Use React.memo.”
❌ Tool without diagnosis

9. When should you lift state up vs keep it local?

Strong Answer

Lift when:
  • Shared across components
Keep local when:
  • Used in one place
Trade-off:
  • Lifting → coupling
  • Local → duplication risk

Weak Answer

“Lift when needed.”
❌ Too vague

10. Explain how React batches state updates in modern React.

Strong Answer

  • React batches updates across events and async boundaries
  • Uses scheduler to optimize rendering

Weak Answer

“React combines updates.”
❌ Lacks depth

11. What is a race condition in state updates and how do you prevent it?

Strong Answer

Example:
  • Multiple API calls → out-of-order responses
Fix:
  • Track request ID / cancel previous

Weak Answer

“Use async/await.”
❌ Doesn’t solve race condition

12. Why is setting state inside render dangerous?

Strong Answer

  • Causes infinite loop
  • Violates render purity

Weak Answer

“It’s not allowed.”
❌ No explanation

13. How does useState behave differently from class setState?

Strong Answer

  • useState replaces value
  • Class setState merges object

Weak Answer

“Hooks are better.”
❌ Not technical

14. How would you design state for a complex form?

Follow-up

  • Trade-offs between flat vs nested state?

Strong Answer

  • Flat structure preferred
  • Easier updates and validation

Weak Answer

“Use one object.”
❌ No nuance

15. Explain a scenario where state causes performance bottleneck.

Strong Answer

  • Frequent updates (e.g., typing)
  • Large component tree re-render
Fix:
  • Debounce
  • Split components

Weak Answer

“Too many states.”
❌ Not specific

16. Why does React recommend immutability?

Strong Answer

  • Enables efficient diffing
  • Predictable updates

Weak Answer

“Because React says so.”
❌ No reasoning

17. How would you handle state that depends on previous state in async flows?

Strong Answer

  • Always use functional updates

Weak Answer

“Use useEffect.”
❌ Not addressing dependency issue

18. What are common mistakes when syncing props to state?

Strong Answer

  • State doesn’t update automatically
  • Causes stale UI
Fix:
  • Avoid duplication or use effect

Weak Answer

“Use state for props.”
❌ Misunderstanding

19. How would you model state for a real-time UI (chat, typing indicator)?

Strong Answer

  • Separate transient vs persistent state
  • Handle race conditions
  • Minimize re-renders

Weak Answer

“Use WebSocket.”
❌ Not about state design

20. What signals indicate your state design is flawed?

Strong Answer

  • Frequent bugs syncing values
  • Too many dependencies in effects
  • Re-renders hard to control
  • Duplicate data

Weak Answer

“Code is messy.”
❌ Not actionable

Final Interview Insight

A senior engineer should demonstrate:
  • Mental model of React internals
  • Clear reasoning behind decisions
  • Ability to debug real-world issues
  • Trade-off awareness (performance vs simplicity)