ReactJS Mastery Quiz.
85+ expert-level questions covering React Fiber, Modern Hooks, and Next.js 15 Server Components.
Course Overview: Frontend Systems Design
Module 01 : Core Internals & Fiber Architecture
The old Stack reconciler (used prior to React 16) operated synchronously. Once it started updating the component tree, it couldn't stop until it was finished. If the component tree was deep and complex, this synchronous traversal would block the main thread, causing dropped frames and sluggish interactions (the 'jank' effect). Fiber completely rewrote React's core algorithm. It allows React to break rendering work into small units and spread it over multiple frames (a technique called time-slicing). Crucially, Fiber can pause, resume, and prioritize work. For example, a high-priority update like typing in an input field can interrupt a low-priority update like rendering a massive list of data. This architectural shift is the foundation for all modern React features, including Concurrent Mode, Suspense, and smooth UI transitions.
These two concepts solve entirely different problems. **Shadow DOM** is a native browser API (part of Web Components) designed to encapsulate styling and markup. It creates a scoped subtree isolated from the main document, ensuring that global CSS doesn't accidentally style internal component elements. **Virtual DOM**, on the other hand, is a lightweight JavaScript representation of the real DOM maintained by libraries like React. Modifying the real DOM is incredibly slow because it triggers browser layout calculations and repaints. React instead updates the Virtual DOM, computes the exact differences ('diffing'), and then selectively applies only the necessary changes to the real DOM ('reconciliation'). While Shadow DOM is for styling isolation, Virtual DOM is an optimization technique for rendering performance.
Concurrent Mode (now referred to as Concurrent React) allows React to work on multiple versions of the UI simultaneously. Before this, updates were always blocking. In Concurrent React, rendering is interruptible. Imagine a search input that filters a list of 10,000 items. If filtering blocks the main thread, typing feels incredibly laggy because the browser can't paint the keystrokes until the list finishes rendering. With Concurrent React (via `useTransition` or `useDeferredValue`), you can mark the list rendering as a low-priority transition. React will start rendering the list in the background but will instantly pause that work to process every keystroke you type, ensuring the input stays buttery smooth. This drastically improves perceived performance and overall User Experience (UX) without requiring complex web worker architectures.
Module 02 : Recruiter's Screening Room (Junior & Confirmed)
Historically, functional components were 'dumb' or stateless — they simply took props and returned JSX. To manage state or hook into lifecycle events (like `componentDidMount`), developers were forced to use ES6 Class components. The introduction of Hooks (React 16.8) fundamentally changed this. Hooks (`useState`, `useEffect`) allow functional components to handle complex state and side effects without the overhead of classes or the notorious confusion around the `this` keyword. Today, Functional components are the absolute modern standard. Class components are considered legacy; while still supported by React, new code should universally use functions to maximize composability and take advantage of React's latest compiler optimizations.
Hooks are incredibly powerful but rely on strict architectural constraints to work correctly. 1. **Only call Hooks at the top level.** You must never call Hooks inside loops, conditions, or nested functions. React relies on the exact order in which Hooks are called to associate state with the correct Hook call. If a Hook is conditionally skipped during a re-render, the execution order shifts, causing massive state synchronization bugs. 2. **Only call Hooks from React functions.** You can only call Hooks from a standard React component or from a custom Hook. You cannot call them from regular JavaScript utility functions. This ensures all stateful logic remains tightly bound to React's rendering lifecycle.
// ❌ BAD: Conditional hook execution
if (isLoggedIn) {
useEffect(() => { fetchUserData() }, []);
}
// ✅ GOOD: Condition inside the hook
useEffect(() => {
if (isLoggedIn) fetchUserData();
}, [isLoggedIn]);When rendering arrays of elements in React (usually via `.map()`), you must provide a unique `key` prop to each item. Keys serve as unique identifiers that help React's reconciliation engine track which specific items have changed, been added, or removed over time. Without keys (or worse, using the array index as a key), React struggles to identify elements when the list is reordered or an item is deleted. This often results in a catastrophic bug where state (like an open dropdown or text inside an input field) accidentally 'jumps' to the wrong list item. A good key should be a stable, unique identifier from your database, such as an item ID.
Module 03 : Advanced Hooks & State Logic
Both Hooks solve the same problem—keeping the UI responsive during heavy renders—but they are used from different vantage points. **`useTransition`** is used when you *control the state update*. For example, if you control the `setSearchQuery` function, you can wrap the state update in `startTransition()`. This tells React, 'This specific state change is low priority.' **`useDeferredValue`** is used when you *do not control the state update*—you only receive the value via props. For example, if a parent component passes down a rapidly changing `searchTerm` prop, and the child component renders a heavy visualization based on it, the child can use `useDeferredValue(searchTerm)`. This tells React to render the heavy visualization using the 'old' value while seamlessly preparing the 'new' visualization in the background.
The `eslint-plugin-react-hooks` rule `exhaustive-deps` is often misunderstood by junior developers, who frequently try to disable it to silence warnings. The rule mandates that every reactive value (props, state, or derived variables) referenced inside a `useEffect` must be declared in its dependency array. Ignoring this rule causes **stale closures**. A stale closure occurs when the `useEffect` function captures and locks in variables from a previous render. Because the effect didn't re-run when the state changed, the logic inside the effect continues to operate on severely outdated data. Always fix the dependencies, or refactor your logic to not need the dependency, rather than suppressing the linter.
// ❌ Stale Closure Example (Count will always be 1)
useEffect(() => {
const interval = setInterval(() => {
setCount(count + 1); // Uses 'count' from initial render
}, 1000);
return () => clearInterval(interval);
}, []); // Missing 'count' dependency
// ✅ Solution: Use functional state updates
useEffect(() => {
const interval = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(interval);
}, []);`useLayoutEffect` has a very specific lifecycle: it fires synchronously immediately after DOM mutations have been calculated, but **before the browser has a chance to paint the screen**. Because it runs synchronously on the main thread, it completely blocks the browser from painting until the effect finishes executing. If you run heavy logic or complex DOM measurements inside `useLayoutEffect`, the user will experience noticeable visual stutter or 'jank'. You should almost always default to `useEffect`, which fires asynchronously *after* the browser has painted, ensuring the UI feels immediately responsive. Only use `useLayoutEffect` to avoid visual flickering when you absolutely must measure a DOM element and immediately mutate the DOM based on that measurement.
Module 04 : Next-Gen Architecture (RSC & Next.js)
React Server Components (RSC) are a paradigm shift in how React applications are built. Traditionally, all React components were sent to the browser, requiring the client to download large JavaScript bundles, parse them, fetch data over the network, and finally render the UI. RSCs execute exclusively on the server at build time or request time. They never ship their JavaScript to the client. This allows developers to safely access backend resources directly (like databases or file systems) without building intermediary REST APIs or exposing API keys. Crucially, because their code never hits the browser, RSCs can utilize massive third-party libraries (like heavy markdown parsers) while keeping the client-side bundle size at absolute zero. They are seamlessly interleaved with interactive Client Components for a best-of-both-worlds architecture.
In modern Next.js (App Router) and React 19, components default to being Server Components. The `'use client'` directive is a strict boundary marker. When you place it at the absolute top of a file, you are explicitly telling the bundler, 'This component, and everything it imports, must be bundled and sent to the browser.' You only use `'use client'` when a component requires interactivity or browser APIs. Specifically, you need it if the component uses state (`useState`), effects (`useEffect`), custom hooks, event listeners (`onClick`), or browser-only APIs like `window.localStorage`. To maximize performance, developers should push `'use client'` as far down the component tree as possible, keeping the majority of the application running on the server.
Streaming Hydration completely resolves the traditional 'All or Nothing' problem of Server-Side Rendering (SSR). In standard SSR, the server must wait for all data to load before sending the HTML, meaning the user stares at a blank screen during slow database queries. With Streaming, the server sends a skeleton of the HTML immediately, greatly accelerating the First Contentful Paint (FCP). As asynchronous data resolves on the server, React streams the finished HTML chunks down the wire using HTTP chunked transfer encoding, smoothly swapping out the loading skeletons with final content. Furthermore, React can begin hydrating (attaching event listeners to) the components that have arrived, even while the rest of the page is still streaming. This dramatically improves Largest Contentful Paint (LCP) and Time to Interactive (TTI), keeping users engaged.
Advanced Insight: Next.js 15 PPR
Partial Prerendering (PPR) is the biggest breakthrough in Next.js 15. It combines the speed of static site generation with the flexibility of dynamic rendering in a single page request.
// Minimal PPR Implementation
export const experimental_ppr = true;
export default function Page() {
return (
<main>
<StaticHeader />
<Suspense fallback={<Skeleton />}>
<DynamicUserDashboard />
</Suspense>
</main>
);
}With PPR enabled, the StaticHeader is served instantly from the edge (CDN), while the DynamicUserDashboard is streamed in as soon as the server finishes rendering it. This eliminates the "All or Nothing" choice between Static and Dynamic rendering.
React Architecture FAQ
The React Compiler (formerly React Forget) statically analyzes your component code at build time and automatically inserts memoization where needed. This means developers no longer need to manually use useMemo, useCallback, or React.memo — the compiler determines optimal re-render boundaries by analyzing data flow dependencies and automatically caches expensive computations and stable callback references.
SSR renders the entire page to HTML on the server, then the client re-processes (hydrates) everything. Server Components render individual components on the server and stream their output as a serialized React tree. The critical difference: Server Components never ship their JavaScript to the client, reducing bundle size. They can also directly access databases, file systems, and APIs without exposing credentials to the browser.
Use useState for simple, independent state values (toggle, counter, input text). Use useReducer when: (1) state updates depend on previous state, (2) multiple sub-values are logically related (form with 5+ fields), or (3) state transitions follow predictable patterns that benefit from a centralized dispatch/action model. The reducer function is also easier to test in isolation since it's a pure function.
Server Actions are async functions marked with 'use server' that execute on the server when called from client components. They enable form submissions and data mutations without building separate API endpoints. Under the hood, Next.js creates a POST endpoint for each Server Action, serializes the arguments, executes the function on the server, and returns the result — all with automatic error handling and optimistic UI support.
Concurrent Rendering allows React to interrupt long-running renders to handle higher-priority updates (like user input). With features like useTransition and useDeferredValue, expensive re-renders (filtering 10,000 list items) can be deprioritized so the UI remains responsive. The key insight: React can now prepare new UI in the background without blocking the current screen from responding to user interactions.
The React ecosystem has consolidated: use React's built-in useState/useReducer + Context for app-level state, React Query (TanStack Query) for server state (caching, synchronization, background refetching), and Zustand for complex client-side global state that needs to be accessed outside React's component tree. Redux remains viable for large existing codebases but is no longer the default recommendation for new projects.
Certification of Knowledge
At Kodivio, we serve the engineering community by providing high-signal technical content and private performance tools. Format your advanced React hooks and components locally with our JavaScript Beautifier.