React
Core Concepts
1
What is React and what problem does it solve?
- React is a JavaScript library for building user interfaces through a component-based model.
- It solves the problem of efficiently updating the DOM when application state changes, using a declarative programming model — you describe what the UI should look like, React figures out how to get there.
- Key ideas: Components, unidirectional data flow (props down, events up), and the Virtual DOM.
- Maintained by Meta. Ecosystem includes React DOM (browser), React Native (mobile), React Three Fiber (3D).
What problems does this solve?
- Sets the foundation for every subsequent React concept — interviewers use this to gauge how deep your mental model goes beyond just "knowing the APIs".
2
What is the Virtual DOM and how does React use it?
- The Virtual DOM (VDOM) is an in-memory JavaScript tree that mirrors the real DOM structure.
- When state changes, React creates a new VDOM tree, diffs it against the previous one (reconciliation), and applies only the minimal set of DOM mutations needed.
- This is faster than rebuilding the entire DOM on every change, but the cost is the diffing computation itself.
- React 18 introduced concurrent rendering — work can be paused, resumed, or abandoned, enabling features like Transitions and Suspense without blocking the browser.
What problems does this solve?
- Understanding why React is fast (and when it isn't) shows architectural depth — key for senior roles.
3
What is JSX?
- JSX is a syntax extension for JavaScript that looks like HTML but compiles to
React.createElement()calls. - It is not required but strongly conventional in React codebases.
- Key differences from HTML:
classNameinstead ofclass,htmlForinstead offor, self-closing tags required, camelCase event names (onClick,onChange). - Expressions are embedded with
{'{}'}. JSX is an expression itself — usable in conditionals, functions, arrays.
What problems does this solve?
- JSX is React's primary authoring syntax — understanding what it compiles to demystifies common errors.
4
What is the difference between Props and State?
- Props: Data passed from a parent component. Read-only inside the component. Changing props triggers a re-render of the child.
- State: Data managed internally by a component. Mutable via
useState/useReducer. Changing state triggers a re-render of the component and its descendants. - Key rule: Components should be pure — given the same props and state, they should return the same JSX. Side effects belong in
useEffect. - "Lifting state up" — when two sibling components need to share state, move it to their nearest common ancestor.
What problems does this solve?
- Props vs state is the most fundamental React concept — misunderstanding it leads to bugs and anti-patterns.
Hooks
5
How does useState work?
useState(initialValue)returns[value, setter]. Calling the setter schedules a re-render with the new value.- State updates are asynchronous and batched — you won't see the new value immediately after calling the setter.
- Use the functional updater form when the new state depends on the previous state, to avoid stale closures.
What problems does this solve?
- The stale closure pitfall with useState is one of the most common React bugs in production — essential to understand.
6
How does useEffect work and what are its dependency rules?
useEffect(fn, deps)runsfnafter the browser paints.- No deps array: Runs after every render.
- Empty array
[]: Runs once after mount. - With deps: Runs only when any dependency changes (compared by
Object.is). - Cleanup: Returning a function from the effect runs it before the next effect or on unmount (unsubscribe, clear timers, abort fetch).
- React 18 Strict Mode double-invokes effects in development to surface cleanup bugs.
What problems does this solve?
- useEffect is the most misused hook — understanding cleanup and deps is essential for avoiding memory leaks and stale data.
7
What is useRef used for?
useRef(initialValue)returns a mutable object{'{ current: initialValue }'}that persists across renders without triggering a re-render when changed.- Use case 1 — DOM access: Attach to a JSX element via the
refprop to get the underlying DOM node. - Use case 2 — Mutable values: Store values that need to persist but shouldn't trigger re-renders (e.g., interval IDs, previous values, instance variables).
- Unlike state, reading
ref.currentinside an effect always gives the latest value (no stale closure).
What problems does this solve?
- Refs are the escape hatch from React's declarative model — knowing when to use them vs state is an advanced signal.
8
What are useMemo and useCallback and when should you use them?
useMemo(fn, deps)— memoises the return value offn. Recomputes only when deps change.useCallback(fn, deps)— memoises the function reference itself. Equivalent touseMemo(() => fn, deps).- When to use:
- Pass a callback to a memoised child (
React.memo) to avoid unnecessary re-renders. - Expensive computations that would run on every render.
- Pass a callback to a memoised child (
- Warning: Premature optimisation — always profile first. Memoisation has its own cost (memory + comparison).
What problems does this solve?
- useMemo/useCallback are the most over- and under-used hooks — showing measured judgment about when to apply them is a senior signal.
9
What is useContext?
- Context provides a way to pass data through the component tree without prop-drilling.
- Create context with
createContext(defaultValue). Provide it with<Context.Provider value={'{...}'}>. Consume withuseContext(MyContext). - Caveat: All consumers re-render when the context value changes (referential equality). Wrap values in
useMemoor split contexts to avoid unnecessary re-renders. - Good for: theme, locale, auth, current user. Not a replacement for proper state management at scale.
What problems does this solve?
- Context is the most commonly recommended solution for prop drilling — understanding its performance limitations shows depth.
10
What is useReducer and when do you use it over useState?
useReducer(reducer, initialState)returns[state, dispatch]. State transitions are handled by a pure reducer function:(state, action) => newState.- Use over useState when:
- State logic is complex with multiple sub-values.
- The next state depends on the previous state in a non-trivial way.
- Multiple actions modify the same state — clearer to consolidate in a reducer.
- Pairs naturally with Context to build lightweight Redux-style state management.
What problems does this solve?
- Knowing when to upgrade from useState to useReducer shows maturity in managing complex component logic.
11
What are Custom Hooks?
- A custom hook is a JavaScript function whose name starts with
useand that calls other hooks. - They extract and share stateful logic between components without changing the component hierarchy (no render props or HOCs needed).
- Examples:
useFetch,useLocalStorage,useDebounce,useMediaQuery,useOnClickOutside. - Each call to a custom hook has its own isolated state — not shared between components unless explicitly designed that way.
What problems does this solve?
- Custom hooks are the primary code reuse mechanism in modern React — showing you can extract and compose hooks well is a strong interview signal.
12
What is the React component lifecycle (in function components)?
- Mount: Component rendered for the first time.
useEffect(() => {'{ ... }'}, [])runs after paint. - Update: Re-renders when state/props change. Effects with relevant deps re-run; cleanup of previous effect runs first.
- Unmount: Component removed from tree. Cleanup functions of all effects run.
- Class equivalents:
componentDidMount→ empty-dep effect;componentDidUpdate→ effect with deps;componentWillUnmount→ effect cleanup. - Use
useLayoutEffect(fires synchronously before paint) for DOM measurement; preferuseEffectfor everything else.
What problems does this solve?
- Understanding the component lifecycle is foundational for correctly using effects, avoiding memory leaks, and debugging render issues.
Patterns & Performance
13
What are Controlled and Uncontrolled Components?
- Controlled: Form element value is driven by React state. Every keystroke updates state and React re-renders. Single source of truth.
- Uncontrolled: Form element manages its own value in the DOM. React reads it via a ref when needed (e.g., on submit). Simpler for non-interactive forms.
What problems does this solve?
- Form handling is one of the most common UI tasks — controlled vs uncontrolled is a foundational decision every React dev must understand.
14
What is Component Composition in React?
- Composition is the preferred pattern for code reuse — building complex components from simpler ones rather than inheritance.
- Children pattern: Pass JSX as
childrenfor generic container components (Modal,Card). - Render props: Pass a function as a prop that returns JSX, giving the parent control over what to render.
- Compound components: A set of components that share implicit state via context (e.g.,
Tabs,TabPanel). - React explicitly recommends composition over inheritance for component hierarchies.
What problems does this solve?
- Mastery of composition patterns is a clear senior React signal — it avoids prop-drilling and brittle inheritance chains.
15
What is React.memo and when should you use it?
React.memo(Component)wraps a component and skips re-rendering if props haven't changed (shallow equality by default).- Useful when a component is expensive to render and its parent re-renders frequently with unchanged props.
- Pass a custom comparison function as the second argument for deep or selective comparison.
- Pitfall: Passing a new object/array/function literal on every render defeats memoisation — pair with
useMemo/useCallback. - Always profile before adding — memo adds overhead and complexity.
What problems does this solve?
- React.memo is the primary tool for preventing unnecessary child re-renders — understanding when it actually helps vs hurts is critical.
16
How does React's reconciliation algorithm work and why are keys important?
- React's reconciler (Fiber) diffs the new VDOM tree against the previous one. It assumes:
- Elements of different types produce entirely different trees.
- List items with stable
keys can be matched across renders. - Keys must be unique among siblings and stable across renders (avoid using array index as key for reorderable lists).
- Without keys, React falls back to positional matching — leading to wrong UI state when list items are reordered or removed.
What problems does this solve?
- The key prop is one of the most common sources of subtle list-rendering bugs in React apps.
17
What are Error Boundaries?
- Error boundaries are class components that catch JavaScript errors anywhere in their child component tree, log them, and render a fallback UI instead of crashing.
- Implemented via
static getDerivedStateFromError()andcomponentDidCatch(). - They do not catch errors in: event handlers, async code, SSR, or the boundary itself.
- Libraries like
react-error-boundaryprovide a hook-friendly wrapper. - React 19 will introduce a native
useErrorBoundaryhook.
What problems does this solve?
- Error boundaries are essential for production resilience — a crashed component shouldn't take down the entire app.
18
What are React Portals?
ReactDOM.createPortal(children, domNode)renders children into a different part of the DOM tree outside the component hierarchy.- Event bubbling still follows the React component tree (not the DOM tree).
- Use cases: Modals, tooltips, dropdowns — elements that need to escape
overflow: hiddenor stacking context of a parent.
What problems does this solve?
- Portals solve the common modal z-index/overflow problem cleanly without hacks — showing you know them demonstrates real-world React experience.
19
What are Suspense and lazy loading in React?
React.lazy(() => import('./Component'))enables code splitting at the component level — the bundle is loaded on demand.<Suspense fallback={'{ }'}>shows a fallback while the lazy component loads.- In React 18, Suspense also works with async data fetching (via frameworks like Next.js or libraries like Relay/React Query).
- Streaming SSR + Suspense: Allows the server to send HTML progressively as components resolve.
What problems does this solve?
- Code splitting via Suspense is one of the most impactful performance tools in React — a must-know for any production React developer.
20
What are React Server Components (RSC)?
- React Server Components render on the server only and are never shipped to the client as JavaScript — reducing bundle size.
- Can directly access databases, filesystems, or server-only secrets.
- Client Components (marked with
"use client") retain interactivity, hooks, and event listeners. - RSC and Client Components can be interleaved — RSC can pass serialisable props to Client Components.
- Currently primary usage is through Next.js 13+ App Router.
asynccomponents are supported in RSC.
What problems does this solve?
- RSC is the biggest architectural shift in React since Hooks — understanding the server/client boundary is essential for modern React jobs.
Ecosystem & Advanced
21
What are the main state management options in React?
- useState / useReducer + Context: Built-in. Good for small-to-medium apps. Context has re-render caveats.
- Zustand: Minimal, unopinionated. Simple API, good performance, no boilerplate.
- Redux Toolkit: Structured, predictable. Best for large teams / complex state with side effects (RTK Query).
- Jotai / Recoil: Atomic state model — fine-grained subscriptions avoid unnecessary re-renders.
- Valtio: Proxy-based mutable state — feels imperative, React handles reactivity.
- Server state (async data) is increasingly handled separately by TanStack Query / SWR / RTK Query.
What problems does this solve?
- Knowing the ecosystem landscape and being able to justify a choice for a given context shows experience beyond hobby projects.
22
What is React Query (TanStack Query) and why use it?
- TanStack Query is a server-state management library — handles fetching, caching, synchronising, and updating data from APIs.
- Features: background refetching, cache invalidation, pagination, optimistic updates, loading/error states, deduplication of concurrent requests.
useQueryfor reads;useMutationfor writes;QueryClientmanages cache globally.- Key insight: Server state (async, shared, can become stale) is fundamentally different from client state (synchronous, local). Conflating them leads to complex, fragile code.
What problems does this solve?
- React Query has become a de-facto standard for data fetching — showing familiarity with it is expected in modern React interviews.
23
How does React Router work?
- React Router v6 provides client-side routing via
BrowserRouter,<Routes>,<Route path='...' element={'{...}'}>. - Hooks:
useNavigate(programmatic navigation),useParams(URL params),useSearchParams(query strings),useLocation. - Nested routes: Outlet renders the child route's component in a parent layout.
- Loaders & Actions (v6.4+): Co-locate data fetching and mutations with routes — similar to Next.js route conventions.
What problems does this solve?
- React Router is the standard routing solution for SPAs — understanding its v6 changes and hooks is expected.
24
What is forwardRef and when do you need it?
- By default, the
refprop is not passed through to a child component.React.forwardReflets a component expose its internal DOM node (or another ref value) to its parent. - Use cases: Building reusable input, button, or dialog components that need to be focusable/scrollable from a parent.
What problems does this solve?
- forwardRef is essential when building a component library — any reusable input or focusable component will need it.
25
What is useImperativeHandle?
useImperativeHandle(ref, () => ({'{...}'}), deps)customises the value exposed to the parent via a ref — used withforwardRef.- Instead of exposing the raw DOM node, you can expose specific methods (e.g.,
focus,scrollTo,reset) while keeping other internals private.
What problems does this solve?
- useImperativeHandle is the final escape hatch for imperative APIs — knowing it shows you understand the full ref system.
26
What does React.StrictMode do?
- StrictMode is a development-only wrapper that highlights potential problems in an app.
- In React 18, it:
- Double-invokes component functions, reducers, and effects (mount → unmount → remount) to surface missing cleanups.
- Detects deprecated API usage.
- Warns about impure render functions with side effects.
- Has no effect in production.
- The double-mounting behaviour often surprises developers who see API calls fire twice — this is expected and expected to have proper cleanup.
What problems does this solve?
- StrictMode surprises many developers — understanding why effects fire twice in development prevents wasted debugging time.
27
What are key React performance optimisation patterns?
- Code splitting:
React.lazy+ dynamic imports to reduce initial bundle. - Memoisation:
React.memo,useMemo,useCallback— but only when profiling shows it's needed. - Virtualisation: Render only visible items in long lists (react-virtual, react-window).
- State colocation: Keep state as close to where it's used as possible — avoid lifting state too high.
- Avoid anonymous functions in JSX: They create new references on each render (though React compiler may handle this in the future).
- Transition API (React 18):
startTransitionmarks non-urgent updates — keeps UI responsive during expensive renders. - Always use the React DevTools Profiler to identify actual bottlenecks before optimising.
What problems does this solve?
- Performance optimisation is a senior-level topic — showing you reach for the profiler before applying fixes demonstrates engineering maturity.
28
How do you test React components?
- React Testing Library (RTL): Query the DOM as a user would (by role, label, text). Preferred approach — tests implementation-agnostic behaviour.
- Vitest / Jest: Test runner. RTL uses
@testing-library/jest-domfor custom assertions. - User Interactions: Use
@testing-library/user-eventfor realistic event simulation overfireEvent. - Mocking:
vi.mock/jest.mockfor modules;msw(Mock Service Worker) for API calls. - E2E: Playwright or Cypress for full integration tests against a running app.
- Testing philosophy: test behaviour, not implementation details.
What problems does this solve?
- Testing strategy is a senior signal — knowing RTL's philosophy and when to mock vs test real implementations shows production experience.
29
What are the core concepts of Next.js?
- Next.js is the most popular React meta-framework — provides file-based routing, SSR, SSG, ISR, and React Server Components.
- App Router (Next.js 13+): Directory-based routing using
app/. All components are Server Components by default."use client"opts into client components. - Rendering modes: SSR (per request), SSG (at build), ISR (regenerates on a schedule or on-demand), client-side.
- Special files:
layout.tsx,page.tsx,loading.tsx,error.tsx,not-found.tsx,route.ts(API routes). - Data fetching:
fetchwithcache/revalidateoptions in Server Components;generateStaticParamsfor static routes.
What problems does this solve?
- Next.js is the standard production React framework — understanding its rendering model is expected in most frontend interviews today.
30
What is the difference between Class and Function Components?
- Class components: Extend
React.Component. Lifecycle via methods (componentDidMount, etc.). State viathis.state. Verbose. Can be error boundaries. - Function components + Hooks: The modern standard since React 16.8. Simpler, composable, easier to test. Hooks replace all lifecycle methods.
- When you still see class components: Error boundaries (no hook equivalent yet), legacy codebases.
- The React team has stated there are no plans to remove class components, but all new APIs target function components.
What problems does this solve?
- A fast, confident answer here shows historical context — important for working on any existing React codebase.