Skip to main content

React Interview Questions - Whiteboard

Fundamental Concepts

1. What is Virtual DOM and how does it work?

Answer: The Virtual DOM is a lightweight JavaScript representation of the actual DOM. React maintains a virtual copy and uses a diffing algorithm to:

  1. Compare new virtual DOM with previous version
  2. Calculate minimal changes needed
  3. Batch update only changed parts to real DOM

This is faster than direct DOM manipulation because DOM operations are expensive.

2. Explain React's reconciliation process

Answer: Reconciliation is how React updates the DOM. When state changes:

  1. React creates new Virtual DOM tree
  2. Compares with previous tree (diffing)
  3. Determines minimum number of operations
  4. Updates only changed nodes in real DOM

Key assumptions:

  • Different elements = different trees
  • Stable keys identify list items

3. What are keys in React and why are they important?

Answer: Keys help React identify which items changed, added, or removed. They should be:

  • Stable (don't change between renders)
  • Unique among siblings
  • Not random or index (unless list is static)
// ❌ Bad
{
items.map((item, index) => <Item key={index} />);
}

// ✅ Good
{
items.map(item => <Item key={item.id} />);
}

Hooks

4. Explain useState and when to use useReducer instead

Answer:

  • useState: Simple state, independent values
  • useReducer: Complex state with multiple sub-values, state logic that involves previous state
// useState - simple
const [count, setCount] = useState(0);

// useReducer - complex
const [state, dispatch] = useReducer(reducer, initialState);

5. What is the difference between useMemo and useCallback?

Answer:

  • useMemo: Memoizes computed VALUE
  • useCallback: Memoizes FUNCTION reference
const value = useMemo(() => expensiveCalc(a, b), [a, b]);
const callback = useCallback(() => doSomething(), []);

6. Explain useEffect cleanup and when it runs

Answer: Cleanup runs:

  1. Before effect runs again (dependencies changed)
  2. When component unmounts
useEffect(() => {
const subscription = subscribe();

return () => {
subscription.unsubscribe(); // Cleanup
};
}, []);

Component Patterns

7. Controlled vs Uncontrolled Components

Answer: Controlled: React controls form state

const [value, setValue] = useState('');
<input value={value} onChange={e => setValue(e.target.value)} />;

Uncontrolled: DOM controls state via refs

const ref = useRef();
<input ref={ref} />;
// Access via ref.current.value

8. When to use Context vs Props?

Answer: Props:

  • Parent to direct child
  • 1-2 levels deep
  • Explicit data flow

Context:

  • Deep component trees
  • Global state (theme, auth, language)
  • Avoid prop drilling

Performance

9. How to prevent unnecessary re-renders?

Answer:

  1. React.memo - Memoize component
  2. useMemo - Memoize values
  3. useCallback - Memoize functions
  4. Keys - Proper list keys
  5. Code splitting - Lazy load
  6. Virtualization - Long lists

10. What causes infinite loops in useEffect?

Answer: Common causes:

  1. Missing dependency array
  2. Objects/arrays in dependencies (new reference each render)
  3. Setting state that's in dependencies
// ❌ Infinite loop
useEffect(() => {
setCount(count + 1);
}); // No deps!

// ❌ Infinite loop
useEffect(() => {
setCount(count + 1);
}, [count]); // count changes → effect runs → count changes...

// ✅ Correct
useEffect(() => {
setCount(c => c + 1);
}, []); // Empty deps

Advanced

11. Explain React Fiber

Answer: Fiber is React's reconciliation engine that enables:

  • Incremental rendering (pause/resume work)
  • Priority-based updates
  • Concurrent features
  • Better error handling

12. What are Server Components?

Answer: React Server Components (React 19):

  • Run on server only
  • Zero bundle size
  • Direct database access
  • Automatic code splitting
  • Better performance

Coding Questions

13. Implement usePrevious hook

function usePrevious\<T\>(value: T): T | undefined {
const ref = useRef\<T\>();

useEffect(() => {
ref.current = value;
}, [value]);

return ref.current;
}

14. Implement useDebounce hook

function useDebounce\<T\>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);

return debouncedValue;
}

15. Implement useToggle hook

function useToggle(initial: boolean = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []);
return [value, toggle] as const;
}

System Design

16. Design a scalable React application architecture

Answer:

src/
├── components/
│ ├── common/ # Reusable UI
│ └── features/ # Feature-specific
├── pages/ # Route components
├── hooks/ # Custom hooks
├── services/ # API calls
├── store/ # State management
├── types/ # TypeScript types
├── utils/ # Utilities
└── tests/ # Test files

17. How to handle authentication in React?

Answer:

  1. Context for auth state
  2. Protected routes
  3. Token storage (httpOnly cookies or secure storage)
  4. Refresh token logic
  5. Redirect on auth failure
<Route
path='/dashboard'
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>

Best Practices

  1. Use TypeScript for type safety
  2. Keep components small and focused
  3. Extract custom hooks for reusable logic
  4. Use proper keys in lists
  5. Memoize expensive calculations
  6. Handle loading and error states
  7. Test components with React Testing Library
  8. Optimize bundle size with code splitting
  9. Use ESLint and Prettier
  10. Follow naming conventions