⚛ React Interview Cheat Sheet
For Java Full-Stack Developers (7+ yrs) with 1–2 yrs React Experience
Covers: Core Concepts • Hooks • State Management • Performance • Testing • Architecture
1. React Fundamentals
Q: What is React and how does it differ from Angular/Spring MVC pattern you know?
React is a declarative, component-based UI library (not a full framework). Key differences from MVC:
• Virtual DOM: React maintains a lightweight copy of the real DOM; diffs changes and batches
updates efficiently.
• Unidirectional Data Flow: Data flows parent → child (unlike two-way binding in Angular).
• Component = View only: React handles only the V in MVC. You choose your own state/routing libs.
• JSX: HTML-like syntax compiled to [Link]() calls.
💡 Java Dev Tip: Think of React components like Spring @Service beans — reusable, single-
responsibility units. But React renders UI, not business logic.
Q: Explain the Virtual DOM and reconciliation process.
Virtual DOM (VDOM) is an in-memory JS object representation of the real DOM.
• Step 1 – Render: React creates a new VDOM tree on state/prop change.
• Step 2 – Diffing: React's diffing algorithm (O(n)) compares old vs new VDOM.
• Step 3 – Reconcile: Only changed nodes are updated in the real DOM (batched).
The 'key' prop helps React identify list items across renders to optimize diffing.
// Keys must be stable & unique among siblings
{[Link](item => <li key={[Link]}>{[Link]}</li>)}
💡 Java Dev Tip: Like Hibernate's dirty checking — tracks what changed and syncs only deltas to DB.
Here React syncs VDOM deltas to real DOM.
Q: What is JSX? Is it mandatory?
JSX is syntactic sugar for [Link](). It is NOT mandatory but strongly recommended.
// JSX
const el = <h1 className='title'>Hello</h1>;
// Compiled to:
const el = [Link]('h1', {className:'title'}, 'Hello');
JSX rules: className instead of class, camelCase attributes, single root element (or Fragment <>).
Q: Class Components vs Functional Components — which to use and why?
• Functional Components (Recommended): Plain JS functions returning JSX. Use Hooks for
state/lifecycle.
• Class Components (Legacy): ES6 classes extending [Link]. Use [Link], lifecycle
methods.
// Functional (modern — prefer this)
const Counter = () => {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c+1)}>{count}</button>;
};
Functional components have less boilerplate, easier testing, better tree-shaking, and full Hook support.
💡 Java Dev Tip: Like choosing between verbose Java EE Session Beans vs clean Spring
@Component. Always prefer the modern, concise approach.
Q: What are Props vs State?
• Props: Read-only data passed from parent to child. Immutable inside component. Like method
parameters.
• State: Component's own mutable data. Changes trigger re-render. Like instance variables.
// Props — received, never modified
const Greeting = ({ name }) => <h1>Hello, {name}</h1>;
// State — owned and mutable
const [user, setUser] = useState(null);
Props down, events up — the core React data pattern.
💡 Java Dev Tip: Props = method arguments (immutable). State = instance fields (mutable). Never
mutate props, just like you wouldn't reassign a final param.
2. React Hooks (Critical Topic)
Q: What are Hooks? Why were they introduced?
Hooks are functions (prefixed use*) that let functional components use React features (state, lifecycle,
context).
Introduced in React 16.8 to solve: logic reuse without HOCs/render props, complex class lifecycle
methods, confusing 'this' binding.
Rules of Hooks:
• Rule 1: Call Hooks only at the top level (not inside loops, conditions, nested functions).
• Rule 2: Call Hooks only from React functional components or custom Hooks.
💡 Java Dev Tip: Like Java's default interface methods — added later to solve real pain points without
breaking existing code.
Q: Explain useState — common pitfalls?
const [state, setState] = useState(initialValue);
setState triggers re-render. For objects/arrays, always return a new reference:
// ❌ Wrong — mutating state directly
[Link](newItem); setState(state);
// ✅ Correct — new reference
setState(prev => ({ ...prev, items: [...[Link], newItem] }));
Use functional updater form when new state depends on previous state.
useState with lazy initializer: useState(() => expensiveComputation()) — runs only once.
💡 Java Dev Tip: State immutability = like creating a new object in Java instead of mutating, so
change detection works. React uses [Link]() comparison.
Q: Explain useEffect — dependency array rules?
useEffect runs side effects after render (data fetching, subscriptions, DOM manipulation).
useEffect(() => {
// effect code
return () => { /* cleanup */ };
}, [dep1, dep2]); // dependency array
• No array: Runs after every render.
• Empty array []: Runs once after mount (like componentDidMount).
• With deps: Runs when any dep changes (shallow comparison).
Cleanup function runs before next effect and on unmount.
Always include all values used inside effect in dependency array (use ESLint exhaustive-deps rule).
💡 Java Dev Tip: Like @PostConstruct + @PreDestroy in Spring beans, but reactive — re-runs
whenever declared dependencies change.
Q: useCallback vs useMemo — when to use each?
• useMemo: Memoizes a computed VALUE. Returns cached result until deps change.
const sortedList = useMemo(() => [...data].sort(), [data]);
• useCallback: Memoizes a FUNCTION reference. Returns same function instance until deps change.
const handleClick = useCallback(() => doSomething(id), [id]);
Use useCallback when passing callbacks to child components wrapped in [Link] to prevent
unnecessary re-renders.
Use useMemo for expensive calculations you don't want to repeat on every render.
Don't premature-optimize — profile first with React DevTools.
💡 Java Dev Tip: useMemo ≈ @Cacheable on a method. useCallback ≈ caching the method reference
itself so child components don't see a 'new' function every render.
Q: Explain useRef — use cases beyond DOM access?
useRef returns a mutable ref object { current: value } that persists across renders WITHOUT triggering
re-render.
const inputRef = useRef(null);
<input ref={inputRef} /> // DOM access
[Link]();
• Other use cases:
• Storing previous value: const prevCount = useRef(count); useEffect(() => { [Link] =
count; });
• Interval/timeout IDs: Store timer IDs to clear them on cleanup.
• Skipping first render effect: Use a flag ref to skip initial run.
💡 Java Dev Tip: Like a Java instance variable that doesn't trigger observer notifications when
changed — mutable side-channel storage.
Q: What is useContext? When should you use it vs prop drilling?
useContext reads a Context value without passing props through every intermediate component.
const ThemeContext = createContext('light');
// Provider wraps tree
<[Link] value='dark'>
<App />
</[Link]>
// Consumer anywhere in tree
const theme = useContext(ThemeContext);
Use for: theme, locale, auth user, feature flags — truly global or widely shared state.
Don't over-use: Context re-renders ALL consumers when value changes. For complex state, use
Zustand/Redux instead.
💡 Java Dev Tip: Like Spring's ApplicationContext — provides global beans (values) without manually
injecting through every layer.
Q: Explain useReducer — when over useState?
useReducer manages complex state logic with a reducer function (state, action) => newState.
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'INCREMENT', payload: 1 });
Prefer useReducer when:
• 1.: Next state depends on previous state in complex ways.
• 2.: Multiple sub-values in state object (user, loading, error).
• 3.: State transitions have named actions (easier to debug/test).
Combining useReducer + useContext gives you lightweight Redux-like state management.
💡 Java Dev Tip: Identical pattern to Redux reducer! If you know Redux, useReducer is the same
concept — pure function, immutable state, action dispatch.
Q: How do you create a Custom Hook?
A custom Hook is a JS function starting with 'use' that calls other Hooks — for reusing stateful logic.
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url).then(r => [Link]())
.then(setData).catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
Custom Hooks extract component logic into reusable functions — no HOC or render prop nesting
needed.
💡 Java Dev Tip: Like extracting a Spring @Service from a fat @Controller — same principle of single
responsibility and reusability.
3. Component Patterns & Design
Q: What is [Link]? How does it differ from useMemo?
[Link] is a Higher-Order Component (HOC) that memoizes a component — skips re-render if
props haven't changed.
const MyComponent = [Link](({ name, onClick }) => {
return <div onClick={onClick}>{name}</div>;
});
[Link] = memoizes a COMPONENT (prevents re-render).
useMemo = memoizes a VALUE (inside a component).
Use [Link] for pure presentational components receiving the same props frequently.
Custom comparison: [Link](Component, (prevProps, nextProps) => areEqual)
💡 Java Dev Tip: [Link] ≈ @Cacheable on an entire method's output. Pass the same inputs, get
the same cached output without re-executing.
Q: Explain Higher-Order Components (HOC) pattern.
A HOC is a function that takes a component and returns a new enhanced component.
function withAuth(WrappedComponent) {
return function AuthComponent(props) {
const { isAuth } = useAuth();
if (!isAuth) return <Redirect to='/login' />;
return <WrappedComponent {...props} />;
};
}
export default withAuth(Dashboard);
HOC use cases: authentication guards, logging, error boundaries, data fetching.
Modern preference: Custom Hooks are often simpler for logic reuse. Use HOC when wrapping is
needed.
💡 Java Dev Tip: HOC = Decorator pattern in Java. Like Spring AOP @Around advice — wraps a
component with cross-cutting concerns.
Q: What are Error Boundaries? How do you implement one?
Error Boundaries are class components that catch JS errors in their child tree during render/lifecycle.
They must be class components (no Hook equivalent yet for this).
class ErrorBoundary extends [Link] {
state = { hasError: false };
static getDerivedStateFromError() { return { hasError: true }; }
componentDidCatch(error, info) { logError(error, info); }
render() {
if ([Link]) return <h2>Something went wrong.</h2>;
return [Link];
}
}
Wrap feature sections: <ErrorBoundary><FeatureWidget /></ErrorBoundary>
Does NOT catch: async errors, event handlers, server-side errors.
💡 Java Dev Tip: Like try-catch in Java but for component trees. Think of it as a @ControllerAdvice for
your UI modules.
Q: What is the difference between controlled and uncontrolled components?
• Controlled: React state is the single source of truth. Input value bound to state.
const [val, setVal] = useState('');
<input value={val} onChange={e => setVal([Link])} />
• Uncontrolled: DOM is source of truth. Access via ref.
const ref = useRef();
<input ref={ref} defaultValue='initial' />
// read: [Link]
Prefer controlled for: validation, conditional disabling, formatting, form submission.
Uncontrolled useful for: file inputs, integrating non-React libraries.
Q: Explain React Portals.
Portals render children into a DOM node outside the parent component hierarchy.
import { createPortal } from 'react-dom';
return createPortal(
<div className='modal'>{children}</div>,
[Link]('modal-root')
);
Use cases: Modals, tooltips, dropdowns that need to escape CSS overflow:hidden or z-index stacking
contexts.
Event bubbling still works through React's virtual DOM tree (not the DOM tree).
4. State Management
Q: When to choose Context API vs Redux vs Zustand?
• Context API: Good for low-frequency updates (theme, auth, locale). Simple, built-in. Re-renders all
consumers.
• Redux Toolkit: Best for large apps, complex state transitions, strong DevTools, time-travel
debugging. More boilerplate.
• Zustand: Lightweight (1KB), minimal boilerplate, selective subscriptions, no Provider needed.
Growing preference.
• Recoil / Jotai: Atomic state models, fine-grained subscriptions for derived state.
Decision guide:
• Small app / few global values: Context + useReducer
• Medium app: Zustand
• Large enterprise / team: Redux Toolkit
💡 Java Dev Tip: Context = @ApplicationScoped CDI bean. Redux = Event sourcing with a central
event store. Zustand = lightweight singleton service with reactive subscriptions.
Q: Explain Redux flow: Action → Reducer → Store → View.
• Action: Plain object describing what happened: { type: 'ADD_ITEM', payload: item }
• Reducer: Pure function (state, action) => newState. No side effects.
• Store: Single source of truth holding the entire app state tree.
• Dispatch: [Link](action) sends action to reducer.
• Selector: Reads specific slice from store.
// Redux Toolkit slice (modern approach)
const cartSlice = createSlice({
name: 'cart',
initialState: [],
reducers: {
addItem: (state, action) => { [Link]([Link]); }
}
});
Redux Toolkit uses Immer under the hood — allows 'mutating' syntax that's actually immutable.
💡 Java Dev Tip: Redux flow ≈ CQRS pattern. Actions = Commands. Reducer = Command Handler.
Store = Read Model. Unidirectional, predictable, auditable.
Q: What is React Query (TanStack Query)? How does it differ from Redux for server
state?
React Query manages SERVER STATE — async data from APIs (fetching, caching, synchronization,
background updates).
Redux manages CLIENT STATE — UI state, user selections, feature flags.
const { data, isLoading, error } = useQuery({
queryKey: ['users', userId],
queryFn: () => fetch(`/api/users/${userId}`).then(r => [Link]())
});
React Query gives you: automatic caching, background refetching, stale-while-revalidate, pagination,
optimistic updates.
Modern recommendation: React Query for server state + Zustand/Context for client state.
💡 Java Dev Tip: React Query ≈ Spring Cache + @Scheduled refresh on your REST call results.
Eliminates the loading/error/data boilerplate you'd manually write in Redux.
5. Performance Optimization
Q: What causes unnecessary re-renders? How do you prevent them?
Re-renders occur when: state changes, parent re-renders, context value changes.
Unnecessary re-renders — child re-renders despite same props:
• Fix 1 – [Link]: Wrap component to skip render if props unchanged.
• Fix 2 – useCallback: Stabilize function references passed as props.
• Fix 3 – useMemo: Stabilize object/array references passed as props.
• Fix 4 – State colocation: Move state down — only subtree re-renders, not whole tree.
• Fix 5 – Context splitting: Separate frequently-changing context from stable context.
Profile first with React DevTools Profiler before optimizing!
💡 Java Dev Tip: Re-render = method call you didn't need to make. Optimization = eliminate
unnecessary work, not premature micro-optimization.
Q: Explain Code Splitting and Lazy Loading in React.
Code splitting breaks your bundle into smaller chunks loaded on demand.
import React, { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard'));
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
[Link] + Suspense enables route-level or component-level code splitting.
With React Router: wrap each route component in lazy().
Result: Initial bundle smaller → faster Time to Interactive (TTI).
💡 Java Dev Tip: Like lazy-loading JPA @OneToMany — don't fetch what you don't need until
needed. Reduces initial payload size.
Q: What is [Link] and Concurrent Mode?
Suspense lets components 'wait' for something (lazy load, data) before rendering. Shows fallback in
the meantime.
Concurrent Mode (React 18) enables React to interrupt, pause, and resume rendering work.
Key Concurrent features:
• useTransition: Mark state update as non-urgent; keep UI responsive during slow updates.
const [isPending, startTransition] = useTransition();
startTransition(() => setSearchQuery(input));
• useDeferredValue: Defer re-rendering of non-critical part of UI.
• Automatic Batching (React 18): Multiple setState calls in async functions are batched
automatically.
💡 Java Dev Tip: Concurrent Mode ≈ non-blocking async I/O in Java NIO. React can work on multiple
renders without blocking the main thread — prioritizes urgent updates.
Q: What is virtualization / windowing for large lists?
Render only visible items in a large list instead of all DOM nodes.
Libraries: react-window (lightweight), react-virtual (TanStack), react-virtuoso.
import { FixedSizeList } from 'react-window';
<FixedSizeList height={500} itemCount={10000} itemSize={35}>
{({ index, style }) => <div style={style}>Row {index}</div>}
</FixedSizeList>
Renders only ~15 DOM nodes instead of 10,000 — massive performance gain.
Use when list > 100–200 items that users scroll through.
💡 Java Dev Tip: Like pagination in database queries — don't load 10,000 rows to display 20. Only
fetch/render what's in the viewport.
6. Architecture, Routing & Forms
Q: How does React Router v6 work? Key differences from v5?
React Router v6 uses nested routes and element prop instead of component/render.
// v6
<Routes>
<Route path='/' element={<Home />} />
<Route path='/users/:id' element={<UserDetail />} />
<Route path='*' element={<NotFound />} />
</Routes>
Key v6 changes: Routes (plural), element instead of component, relative paths, useNavigate instead of
useHistory, Outlet for nested routes.
const { id } = useParams(); // URL params
const navigate = useNavigate(); // programmatic nav
💡 Java Dev Tip: React Router ≈ Spring MVC @RequestMapping. Routes map URLs to components.
useNavigate() ≈ redirect() in controllers.
Q: Explain React Hook Form vs Formik for form management.
• React Hook Form (RHF): Uncontrolled approach. Minimal re-renders. register() attaches native
inputs.
const { register, handleSubmit, formState: { errors } } = useForm();
<input {...register('email', { required: true, pattern: /^\S+@\S+$/ })} />
• Formik: Controlled approach. More explicit, more re-renders. Mature ecosystem.
Performance: RHF re-renders only on error state change. Formik re-renders on every keystroke.
Validation: Both integrate with Yup/Zod schemas.
Recommendation 2024+: React Hook Form + Zod for type-safe validation.
💡 Java Dev Tip: RHF's register() ≈ JSR-303 Bean Validation annotations. Declarative validation rules
on fields, centralized error handling.
Q: How do you handle API calls in React? Best practices?
• Option 1 – useEffect (basic):
useEffect(() => {
let ignore = false;
fetchUser(id).then(data => { if (!ignore) setUser(data); });
return () => { ignore = true; };
}, [id]);
• Option 2 – React Query (recommended): Handles caching, loading, error, refetch automatically.
• Option 3 – Custom hook: Encapsulate fetch logic in useFetch or useUser hooks.
Best practices: Always handle loading + error states. Abort stale requests. Never fetch in render. Use
services/API layer, not raw fetch everywhere.
💡 Java Dev Tip: Equivalent to Spring @Service layer. Keep API calls out of UI components —
abstract to service functions. Components should only declare what data they need.
7. Testing React Applications
Q: What is React Testing Library? Testing philosophy?
React Testing Library (RTL) tests components from the USER's perspective, not implementation
details.
Philosophy: 'The more your tests resemble the way your software is used, the more confidence they
give you.'
import { render, screen, fireEvent } from '@testing-library/react';
test('increments counter on click', () => {
render(<Counter />);
[Link]([Link]('button', { name: /increment/i }));
expect([Link]('1')).toBeInTheDocument();
});
Query priority: getByRole > getByLabelText > getByText > getByTestId
Avoid: testing state directly, implementation details, internal methods.
💡 Java Dev Tip: RTL = integration testing mindset. Like Spring MockMvc testing the HTTP endpoint
contract, not the internal service call order.
Q: How do you mock API calls in tests?
• MSW (Mock Service Worker) — recommended: Intercepts real network requests at service worker
level.
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
const server = setupServer(
[Link]('/api/users', () => [Link]([{id:1, name:'Alice'}]))
);
beforeAll(() => [Link]());
afterEach(() => [Link]());
afterAll(() => [Link]());
• [Link]() for modules: Mock specific imports.
MSW is preferred: tests your actual fetch/axios code, not mocked wrappers.
💡 Java Dev Tip: MSW ≈ WireMock for React. Stubs HTTP at the network level — same principle as
mocking @RestTemplate with MockRestServiceServer.
Q: What is Vitest and how does it compare to Jest?
Vitest is a Vite-native test runner, Jest-compatible API, faster HMR-based test re-runs.
For Vite-based projects (Vite, create-vite): use Vitest. For CRA/webpack projects: use Jest.
Both work with RTL: just change import from @jest/globals to vitest where needed.
// [Link]
import { defineConfig } from 'vitest/config';
export default defineConfig({ test: { environment: 'jsdom', globals: true } });
Snapshot testing: render(<Component />) → toMatchSnapshot(). Use sparingly for UI regression.
8. Advanced Topics & [Link]
Q: What is Server-Side Rendering (SSR) vs Client-Side Rendering (CSR) vs Static Site
Generation (SSG)?
• CSR: Browser downloads JS bundle, React renders in browser. Slow initial load, fast navigation.
• SSR: Server renders HTML per request, sends to browser. Fast initial paint, SEO-friendly. ([Link]:
getServerSideProps)
• SSG: Pages pre-rendered at build time. Fastest. Good for static content. ([Link]: getStaticProps)
• ISR: Incremental Static Regeneration — regenerate static pages on demand/on schedule.
[Link] App Router (v13+) uses React Server Components (RSC) by default:
'use client' // opt-in to client component
'use server' // server action (form submit, mutation)
💡 Java Dev Tip: SSR ≈ traditional Spring MVC Thymeleaf template rendering. SSG ≈ pre-generating
HTML at deploy time. CSR ≈ SPA where Spring only serves API.
Q: What are React Server Components (RSC)?
RSC run ONLY on the server — no JS sent to client, direct DB/filesystem access, no
useState/useEffect.
Client Components ('use client') run in the browser and handle interactivity.
Composability rule: Server Components CAN render Client Components, but NOT vice versa.
// app/[Link] (Server Component by default in [Link] App Router)
async function Page() {
const data = await [Link]('SELECT * FROM users'); // direct DB access
return <UserList users={data} />;
}
Benefits: Zero bundle size for server-only code, streaming, improved performance.
💡 Java Dev Tip: RSC ≈ Spring MVC controller/service layer running server-side. Client Components
= browser-side JS. Same separation of concerns you already know.
Q: Explain TypeScript with React — key patterns?
TypeScript is strongly recommended for large React apps. Key patterns:
// Props interface
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
const Button: [Link]<ButtonProps> = ({ label, onClick, variant='primary' })
=> ...
// useState with type
const [user, setUser] = useState<User | null>(null);
// Event types
onChange={(e: [Link]<HTMLInputElement>) => setValue([Link])}
Use generics for reusable components. Never use 'any'.
💡 Java Dev Tip: TypeScript in React ≈ Java's type system. Same benefits: compile-time errors, IDE
autocomplete, self-documenting code. Feels natural for Java devs.
Q: What is the React component lifecycle in functional components (using Hooks)?
• Mount: Component renders for first time. useEffect(() => { /* mount logic */ }, []) runs after.
• Update: State/props change → re-render. useEffect with deps runs after relevant changes.
• Unmount: useEffect cleanup function runs: useEffect(() => () => { /* cleanup */ }, []);
Mapping from class to Hooks:
• componentDidMount: useEffect(() => {}, [])
• componentDidUpdate: useEffect(() => {}, [dep])
• componentWillUnmount: useEffect(() => () => cleanup(), [])
• getDerivedStateFromProps: Compute during render directly
• shouldComponentUpdate: [Link] / useMemo
💡 Java Dev Tip: Lifecycle mapping is 1:1 with Spring bean lifecycle. Mount=@PostConstruct,
Unmount=@PreDestroy, Update=@EventListener changes.
9. Quick Reference — Hooks Comparison Table
Hook Purpose When to Use
useState Local component state Any value that changes and triggers re-render
useEffect Side effects Fetch data, subscriptions, DOM manipulation, timers
useContext Consume Context Access theme, auth, locale without prop drilling
useReducer Complex state logic Multiple sub-states, state machines, Redux-like
patterns
useCallback Memoize function ref Callbacks passed to [Link] children
useMemo Memoize computed value Expensive calculations, stable object/array references
useRef Mutable ref (no re-render) DOM refs, storing timers, prev values
useLayoutEffect Sync DOM side effect Measure DOM, synchronous post-render updates
useTransition Mark update as non- Search/filter with heavy re-renders — keep UI
urgent responsive
useDeferredValue Defer non-critical render Show stale value while expensive update runs
10. Common Interview Scenarios for Java Devs
Q: How is React state management different from managing state in a Spring
application?
In Spring: State lives in the database, HTTP session, or @RequestScope beans. Request → Process
→ Response is stateless by design.
In React: State lives in components (useState), global stores (Redux/Zustand), or server cache (React
Query).
Key differences:
• React state is client-side: Lives in browser memory, lost on refresh (unless persisted).
• Immutability required: React detects changes via reference equality. Always return new objects.
• Reactive updates: State changes automatically trigger UI updates — no manual DOM manipulation.
• Lifting state: Share state by moving it to the nearest common ancestor — no singleton service
needed.
💡 Java Dev Tip: Your Java instinct of making services stateless is actually great for React!
Components = stateless where possible, push state up or to a store.
Q: How would you optimize a React app that's loading slowly?
• 1. Measure first: Chrome DevTools Performance tab, React DevTools Profiler, Lighthouse.
• 2. Bundle size: Code splitting with [Link], analyze with webpack-bundle-analyzer.
• 3. Render performance: [Link], useCallback, useMemo, fix unstable object references.
• 4. Network: React Query caching, lazy loading images (loading='lazy'), CDN for assets.
• 5. List performance: Virtualize long lists with react-window.
• 6. Critical path: SSR/SSG with [Link] for faster First Contentful Paint.
• 7. React 18: Enable Concurrent features, Suspense boundaries, useTransition for non-blocking
updates.
💡 Java Dev Tip: Same debugging mindset as Java: profile before optimizing. jProfiler → React
DevTools Profiler. SQL N+1 → unnecessary re-renders. Indexes → memoization.
Q: Explain how you would architect a React app for a large enterprise team.
• Folder structure — Feature-based (not type-based):
src/
features/
user/ # components, hooks, api, types
orders/ # isolated feature module
shared/ # reusable components, utils
lib/ # API client, config
app/ # routing, providers, global setup
• State layers: Server state (React Query) + Client state (Zustand) + UI state (local useState)
• API layer: Centralize all API calls in /lib/api. Never raw fetch in components.
• Component API: Compound components, strict TypeScript props, Storybook documentation.
• Testing: Unit (RTL) + Integration (RTL+MSW) + E2E (Playwright/Cypress).
💡 Java Dev Tip: Same principles as Domain-Driven Design in Java. Feature folders = bounded
contexts. Shared = common library. Dependency direction: features depend on shared, never on each
other.
Bonus: Common Mistakes to Avoid
❌ Mistake ✅ Correct Approach
Mutating state directly: [Link](x) setState(prev => ({...prev, items: [...[Link], x]}))
Missing dependency array in useEffect → infinite Always declare dependencies; use eslint-plugin-
loop react-hooks
Creating objects/arrays inline as props → re-renders Memoize with useMemo or lift outside component
Not cleaning up useEffect (memory leaks) Return cleanup function from useEffect
Using array index as key in dynamic lists Use stable unique IDs as keys
Fetching in render body (not in effect) Always fetch in useEffect or React Query
Overusing useContext for frequently-changing state Use Zustand/Redux for high-frequency updates
❌ Mistake ✅ Correct Approach
prop-drilling 3+ levels deep Lift to Context or state management library
Giant components > 200 lines Split into smaller single-responsibility components
No error handling for async operations Always handle .catch() / error state in UI
Good luck with your React interviews! Your Java foundation is a genuine advantage.
React Interview Cheat Sheet | Generated for Java Full-Stack Developers