FullStack Course LogoFullStack Course
Module: Interview Preparation
Interview Preparation·291·14 MIN READ

291: React and Frontend Architecture Interview Review

TOPICS COVERED: React and Frontend Architecture Interview Review

Learning outcomes

By the end of this lesson, you should be able to:

  • explain and apply rendering and state in a realistic implementation;
  • explain and apply effects in a realistic implementation;
  • explain and apply server state in a realistic implementation;
  • explain and apply routing/forms in a realistic implementation;
  • explain and apply performance in a realistic implementation.

These outcomes are deliberately practical. In an interview, it is not enough to name a hook or a library feature. You need to show that you can choose an approach, implement a small slice, explain its trade-offs, and reason about what happens when the normal path fails.

Prerequisites and retrieval

This lesson assumes the earlier 01–06 foundation and the preceding lessons in this module. Before reading, retrieve one concrete example from a previous project in which one of these concerns appeared. It might be a form that had to preserve user input, a screen that loaded data from an API, or a list that became slow as it grew.

The point of that retrieval exercise is not to memorize terminology. It is to connect the terms to a decision you actually had to make. In a realistic full-stack interview loop, your explanation, trade-offs, debugging approach, code, and project evidence should tell the same story.

Terminology

  • Rendering and state: Explain components, props, and state; reconciliation and keys; derived state; lifting state; reducers and context; refs; and why render should remain pure.
  • Effects: Effects synchronize with external systems, while dependency arrays describe the reactive values that an effect reads.
  • Server state: Use TanStack Query v5 object syntax for query and mutation examples. Be ready to explain query keys, staleness, invalidation, cancellation, optimistic updates, and why fetched data usually should not be copied into client state.
  • Routing/forms: Explain URL state, loaders/actions where applicable, form validation and error states, accessibility semantics, and progressive, user-friendly pending behavior.
  • Performance: Discuss measuring rerenders, code splitting and lazy loading, memoization only when useful, list virtualization, transitions and deferred work, bundle and network cost, and React Compiler/current React capabilities.
  • Testing and accessibility: Prefer behavior- and role-based tests, realistic network boundaries, keyboard and focus coverage, semantic HTML, and targeted E2E tests for critical journeys.

These categories overlap in real applications, but they are not interchangeable. For example, a server response may be rendered by React, yet that does not make it ordinary client state. Keeping the ownership of each concern clear is one of the main signals of sound frontend architecture.

Mental model

Treat React and Frontend Architecture Interview Review as a design problem with observable inputs, outputs, invariants, and failure modes. React interviews should demonstrate judgment about state ownership, rendering and effect semantics, server-state architecture, routing, accessibility, testing, and performance rather than memorized hook recipes. A strong implementation makes its assumptions visible, narrows uncertainty at system boundaries, and leaves enough evidence, such as tests, types, constraints, metrics, or diagrams, to show why the design is safe.

One useful sequence for both an interview answer and production work is:

text
requirement -> constraints -> model -> implementation -> failure analysis -> verification

Start by stating what the feature must do and what must remain true. Then identify constraints: who owns the data, what can fail, how fresh the data must be, and what the user should see while work is pending. Only then choose a React mechanism or library API. A library call can implement a decision, but it cannot make an unstated requirement correct.

Deep dive

1. Rendering and state

The first question is not “Which hook should I use?” It is “What data changes, and which component should own that change?” Components describe UI, props pass values and callbacks down, and state stores information that must survive a render and trigger another render when it changes. Reconciliation is React's process for relating one render's element tree to the next; stable keys help it identify which list item is which across those renders.

Derived state is a frequent source of unnecessary complexity. If a value can be calculated from props or existing state during render, storing a second copy creates two sources of truth and creates synchronization work. Lift state when sibling components need one shared owner. Use a reducer when a state machine has several related transitions or when naming the transitions makes the invariant easier to inspect. Use context to provide a value across a subtree, not as a reason to put every value in one global container.

Refs are useful for values that should persist without causing a render, and for imperative interaction with a DOM node. They are not a replacement for state when the UI must update. Render should remain pure: given the same inputs, it should calculate the same result without performing subscriptions, mutations, or other external work. That property is what lets React start, discard, or repeat rendering safely.

Decision rule: Use rendering and state deliberately when they make the contract or invariant easier to prove. If an approach only reduces typing while hiding an assumption, prefer the more explicit design.

2. Effects

An effect is for synchronizing a component with something outside the render calculation: a subscription, browser API, timer, imperative widget, or other external system. It is not the default place for every calculation that happens after a click or every value that can be derived during render. This distinction is where people usually get confused: an effect can make code run, but it does not automatically make that code the right owner of the behavior.

Dependency arrays describe the reactive values the effect reads. Cleanup must undo the corresponding external work, such as removing a listener, cancelling a subscription, or stopping a timer. Without correct dependencies or cleanup, an effect can capture a stale closure, retain obsolete work, or perform the same synchronization more often than intended. When the work is a direct consequence of an event, an event handler is usually easier to follow. When it is a pure calculation from current inputs, calculate it during render instead.

There is a practical debugging test: identify the external system, the value that should be synchronized, and the operation that reverses the synchronization. If you cannot name those three pieces, the code may not need an effect.

Decision rule: Use effects deliberately when they make the contract or invariant easier to prove. If they only move ordinary calculation or event logic into a less explicit place, choose the simpler mechanism.

3. Server state

Server state has a different lifecycle from local UI state. It can be stale while still being useful, may be shared by multiple views, can be invalidated by a mutation, and can fail independently of the component displaying it. TanStack Query gives those concerns an explicit cache and query lifecycle. Use TanStack Query v5 object syntax for query and mutation examples, and be precise about what the query key identifies.

A query key should describe the resource and the inputs that change its result. Staleness is a freshness policy, not the same thing as a failed request. Invalidation tells the client that cached data should be checked again; cancellation prevents work that is no longer useful, such as a request for a screen the user has left. Optimistic updates can make a mutation feel immediate, but they require a rollback or refetch strategy when the server rejects the change or another update wins.

Do not usually copy fetched data into client state. Doing so creates two owners, makes freshness ambiguous, and forces manual synchronization. Keep genuinely local concerns, such as a draft, selected tab, or open dialog, in client state; let the server-state layer own remote data and its loading, error, stale, and refresh behavior.

Decision rule: Use server state deliberately when its cache, freshness, cancellation, and mutation semantics make the contract easier to prove. If the data is purely local and has no remote lifecycle, ordinary client state is clearer.

4. Routing/forms

The URL is state when a value should survive refresh, be shareable, participate in navigation, or be represented by the browser's history. Search filters, pagination, and selected resources often belong there rather than in an opaque component variable. Loaders and actions, where the routing framework supports them, can keep data loading and form submission close to the route boundary.

Forms need more than a success callback. Define the input contract, validate it at the appropriate boundary, and give the user useful field-level and form-level errors. Client validation improves feedback, but it does not replace server validation because the client can be modified and network input is untrusted. Loading, empty, error, stale, and success states should be intentional rather than accidental consequences of a promise.

Accessibility is part of the form and routing contract. Use semantic HTML, labels, correctly associated errors, keyboard access, and focus behavior that reflects navigation or a failed submission. Pending behavior should preserve the user's context where possible: disable or guard only the controls that must not be repeated, show progress, and make the result of the action understandable.

Decision rule: Use routing and form mechanisms deliberately when they make URL ownership, validation, navigation, accessibility, and pending behavior explicit. If a value is temporary presentation state, do not force it into the URL or a route action.

5. Performance

Performance work starts with measurement. Inspect rerenders and the browser's actual work before adding memoization. A rerender is not automatically a problem; unnecessary expensive work, blocked input, excessive network transfer, and large parsing or scripting costs are the problems to identify.

Code splitting and lazy loading can reduce the initial bundle when a feature is not needed immediately. Memoization is useful when it avoids measured expensive work and the relevant inputs are stable, but it adds its own complexity and is not a universal speed button. For large collections, list virtualization limits how much UI is mounted at once. Transitions and deferred work can keep urgent interactions responsive while non-urgent rendering catches up; they do not make an expensive operation disappear.

Also account for bundle and network cost separately from processing time. A smaller transfer may still leave expensive parsing, scripting, layout, paint, or image decoding. React Compiler and current React capabilities may change how much manual memoization is needed, but they do not remove the need to understand ownership, measure behavior, or choose an appropriate data model.

Decision rule: Use performance techniques deliberately when evidence identifies a bottleneck or risk and the technique addresses it. Do not add a scalable mechanism without a scale requirement or measurement.

6. Testing and accessibility

Tests should exercise the behavior a user or an integration boundary depends on. Prefer queries based on roles and accessible names over implementation details. Use realistic network boundaries so the test can expose loading, success, stale, and failure behavior without coupling every assertion to an internal hook.

Include keyboard and focus coverage for interactive flows, and use semantic HTML so the browser and assistive technology have the information they need. Targeted E2E tests are valuable for critical journeys, but they should complement focused component or integration tests rather than replace every smaller check. The test suite is evidence for the design's invariants: it should make clear what must continue to work when the implementation changes.

Decision rule: Use testing and accessibility practices deliberately when they make observable behavior and user-facing guarantees easier to verify. If a test only confirms an implementation detail, it may be protecting the wrong contract.

Worked example

Consider a realistic full-stack interview loop in which the explanation, trade-offs, debugging, code, and project evidence all need to agree. Start by writing the requirement in one sentence. List the input and output contracts, then identify which concept owns each failure mode. The useful separation is straightforward: parsing and validation belong at the boundary; domain rules belong in the domain or service layer; persistence rules belong in the database or repository; and presentation rules belong in the client. Mixing these concerns can make a happy-path demo shorter, but it makes edge cases much harder to reason about.

text
Prompt -> clarify -> state assumptions -> solve -> test edge cases -> explain trade-offs

Walk through at least four cases: the normal path; an empty or missing value; a duplicate, retry, or concurrent path where relevant; and a dependency failure. For each case, say which layer detects the problem and what the caller observes. For example, do not stop at “the form shows an error.” Identify whether the error came from client validation, a server response, or a dependency timeout, and explain whether the user can correct, retry, or safely continue. That level of specificity is expected in a senior code review or technical interview.

Production perspective

Production correctness is broader than “the code works on my machine.” Ask how the design behaves during deploys, retries, partial failure, stale clients, concurrent requests, malformed data, schema changes, and high-cardinality workloads. Prefer explicit contracts, bounded resource usage, structured errors, and measurable behavior. Optimize only after you can identify the bottleneck or risk with evidence.

When an external dependency is involved, define a timeout and cancellation strategy. When persistence is involved, define transaction and consistency expectations. When the UI exposes the result, define loading, empty, error, stale, and success states. When security is involved, assume the client can be modified and network input is untrusted. Client-side checks improve the experience, but server-side authorization and validation must enforce the guarantee.

Guided lab

Perform a React code-review mock on a deliberately flawed page. The page should contain duplicate server state, unstable keys, an unnecessary effect, an inaccessible modal, and an optimistic mutation bug. Refactor it using TanStack Query v5, and justify each change rather than merely replacing one API call with another.

Complete the lab with this discipline:

  1. Write the requirement and two non-requirements.
  2. List input, output, and error contracts before implementation.
  3. Implement the smallest correct vertical slice.
  4. Add at least one invalid-input test and one edge-case test.
  5. Instrument or inspect the behavior instead of guessing.
  6. Refactor one hidden assumption into an explicit type, constraint, function, or configuration.
  7. Explain one alternative design and why you did not choose it.
  8. Record a short “what would break at 10× scale?” note.

During the review, connect each flaw to an observable consequence. Unstable keys can associate state with the wrong list item; duplicate server state can drift from the cache; an unnecessary effect can create extra synchronization work; an inaccessible modal can trap or lose keyboard focus; and an optimistic mutation can leave the UI claiming success after the server rejects the request. The lab is complete only when the refactoring and its tests demonstrate how those failures are prevented or recovered from.

Edge cases and failure modes

  • Rendering and state: Test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes.
  • Effects: Test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes. Also verify cleanup and behavior when reactive dependencies change.
  • Server state: Test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes. Include stale data, cancellation, rejected mutations, rollback, and invalidation behavior where they apply.
  • Routing/forms: Test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes. Include refresh, back/forward navigation, validation errors, keyboard use, and pending submission behavior.
  • Performance: Test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes. Measure rerenders, transfer, parsing, scripting, and rendering costs instead of treating a single optimization as proof.

The exact edge case depends on the feature, but the reasoning pattern is stable: identify the smallest credible input, the largest credible workload, repeated or competing work, and the dependency that can fail. Then state the expected observable result.

Common mistakes and debugging

  • Solving the example instead of the requirement: a copied pattern can be syntactically correct but architecturally wrong.
  • Hiding uncertainty with assertions, broad exception handlers, permissive schemas, or “temporary” any values.
  • Testing only the happy path and therefore discovering contracts only after integration.
  • Optimizing before measuring, or selecting a scalable mechanism without a scale requirement.
  • Letting client-side behavior stand in for server-side authorization, validation, or persistence guarantees.

For debugging, reproduce the smallest failing case first. Inspect the actual value, render behavior, network request, or execution plan rather than inferring it from the source. Trace the boundary where the invariant first becomes false, then fix the layer that owns that invariant instead of adding a downstream patch. Depending on the symptom, that boundary may be the source or build, browser or DOM, Network or HTTP, server or route, database or query, or deployment or configuration.

Interview questions

  1. What problem does Rendering and state solve, and what trade-off or failure mode would make you choose a different approach?
  2. What problem does Effects solve, and what trade-off or failure mode would make you choose a different approach?
  3. What problem does Server state solve, and what trade-off or failure mode would make you choose a different approach?
  4. What problem does Routing/forms solve, and what trade-off or failure mode would make you choose a different approach?
  5. What problem does Performance solve, and what trade-off or failure mode would make you choose a different approach?

Answer each question with a concrete invariant, not only a definition. A strong answer can name what the user observes, what can fail, how you would measure or test it, and why a plausible alternative is less suitable for the stated constraints.

Checkpoint

Without notes, explain React and Frontend Architecture Interview Review to another developer in five minutes. Your explanation must include one invariant, one edge case, one production failure mode, and one alternative design. Then implement a small example without copying the lesson code. If your explanation cannot identify who owns the data or the failure, return to the mental model and make that boundary explicit.

Mastery checklist

  • I can define the core terms precisely.
  • I can choose a design from requirements instead of from habit.
  • I can implement and test the normal path and edge cases.
  • I can explain the runtime, storage, or complexity cost.
  • I can identify which layer owns validation, errors, and recovery.
  • I can compare at least two reasonable alternatives.
  • I can explain how the design changes at larger scale or stricter reliability.

References

Reader page: /interview-prep/lesson/291/react-and-frontend-architecture-interview-review