Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Introduction to React
React is a JavaScript library for building user interfaces, developed by Facebook (now Meta). It allows developers to build reusable UI components and manage the state of their applications efficiently. At its core, React answers one question: given this data, what should the screen look like? You describe the UI you want (declaratively), and React figures out how to make the browser match that description. This is a fundamentally different approach from imperative DOM manipulation where you write step-by-step instructions (“find this element, change its text, add a class…”).Why React?
- Component-Based: Build encapsulated components that manage their own state, then compose them to make complex UIs. Think of components as LEGO blocks — each one is self-contained, and you snap them together to build anything from a simple button to an entire dashboard.
- Declarative: Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. You say what you want, not how to get there.
- Virtual DOM: React uses a virtual DOM to improve performance. It calculates the minimal set of changes needed to update the actual DOM. Think of it like editing a blueprint before sending the construction crew — you plan all the changes first, then apply them in one efficient batch.
- Ecosystem: A massive ecosystem of libraries, tools, and community support.
How React Works Under the Hood
Understanding what happens behind the scenes helps you write better React code and debug issues effectively.JSX Transformation
JSX is not valid JavaScript—it needs to be transformed before the browser can execute it. Tools like Babel or SWC (used by Vite) compile JSX into regular JavaScript function calls.createElement calls:
jsx-runtime instead of React.createElement, which is slightly more efficient and doesn’t require importing React in every file.The Virtual DOM Explained
The Virtual DOM is a lightweight JavaScript representation of the actual DOM. React uses it to minimize expensive DOM operations. Real-world analogy: Imagine you are an architect renovating a building. Instead of tearing down walls and rebuilding them every time the client changes their mind, you first update the blueprint (Virtual DOM). You then compare the old blueprint with the new one, identify exactly which walls need to move, and send only those changes to the construction crew (the real DOM). That is reconciliation in a nutshell.| Direct DOM Manipulation | React’s Approach |
|---|---|
| Every change = immediate DOM update | Changes batched together |
| DOM operations are slow (layout, paint) | Virtual DOM operations are fast (JS objects) |
| May update more than needed | Only updates what changed (diffing) |
The Reconciliation Algorithm
React’s diffing algorithm has O(n) complexity through these heuristics:- Different element types → Rebuild entire subtree
- Same element type → Update only changed attributes
- Lists with keys → Efficiently reorder/update items
Setting Up a React Project
The easiest way to start a new React project is using Vite (recommended) or Create React App.Using Vite
JSX (JavaScript XML)
JSX is a syntax extension for JavaScript. It looks like HTML, but it allows you to write HTML structures within JavaScript code.Rules of JSX
-
Return a Single Root Element: You must wrap adjacent elements in a parent tag or a Fragment (
<>...</>). -
Close All Tags: Self-closing tags must end with a slash (e.g.,
<img />,<br />). -
camelCase Properties: HTML attributes become camelCase in JSX.
class->classNameonclick->onClicktabindex->tabIndex
-
JavaScript Expressions: You can embed any valid JavaScript expression inside curly braces
{}.
Conditional Rendering
You can use JavaScript operators likeif or the ternary operator inside JSX.
Ternary Operator
Logical AND (&&)
Render something only if a condition is true:Logical OR (||) and Nullish Coalescing (??)
Provide fallback values:Early Returns
For complex conditions, use early returns for cleaner code:Styling in React
React offers multiple ways to style components:Inline Styles
backgroundColor not background-color) and values are strings or numbers.CSS Modules
CSS Modules scope styles locally to the component:Conditional Classes
Common JSX Pitfalls
1. Forgetting to Return JSX
2. Using class Instead of className
3. Forgetting to Close Tags
4. Rendering Objects Directly
5. Incorrect Boolean Attributes
🎯 Practice Exercises
Exercise 1: Create a Profile Card
Exercise 1: Create a Profile Card
ProfileCard component that displays:- A profile image
- Name
- Bio
- A “Follow” button
Exercise 2: Conditional Badge
Exercise 2: Conditional Badge
status prop:- “online” → Green badge
- “away” → Yellow badge
- “offline” → Gray badge
Exercise 3: Render a List of Items
Exercise 3: Render a List of Items
Summary
| Concept | Description |
|---|---|
| React | JavaScript library for building component-based UIs |
| Virtual DOM | Lightweight representation of the DOM for efficient updates |
| JSX | Syntax extension that lets you write HTML-like code in JavaScript |
| Babel/SWC | Compilers that transform JSX into React.createElement calls |
| Vite | Modern, fast build tool for React projects |
| Fragments | <>...</> to group elements without adding DOM nodes |
| Expressions | Use {} to embed JavaScript in JSX |
| Conditional Rendering | Ternary ? :, logical &&, or early returns |
Next Steps
Interview Deep-Dive
Walk me through what happens when React reconciles a list where one item was inserted at the beginning. Why do keys matter here?
Walk me through what happens when React reconciles a list where one item was inserted at the beginning. Why do keys matter here?
key prop. Without keys (or with index-based keys), React compares by position: old position 0 vs new position 0, old position 1 vs new position 1, and so on. If you insert an item at the beginning, every position now has a “different” element, so React tears down and recreates every single list item — even though the original items are identical, just shifted.With stable keys (like database IDs), React matches old key=“a” to new key=“a” regardless of position. It sees that key=“x” is new and creates it, then moves the existing items by adjusting DOM order. This is the difference between O(n) DOM mutations and O(1) insertion plus some cheap reordering.Where this really bites you in production: if your list items have internal state — say, an open dropdown or an in-progress text input — index keys cause that state to “jump” to the wrong item because React reuses the component instance at position 0 for whatever data is now at position 0. I saw this exact bug on a sortable task board where a user was editing a task name, dragged another task above it, and their edit appeared on the wrong task. The fix was switching from index keys to task.id.Follow-up: How does React’s O(n) diffing algorithm compare to a naive tree diff, and what tradeoffs did the React team make to achieve this?A full tree diff between two arbitrary trees is O(n^3) — essentially comparing every node against every other node, then computing minimal edit operations. That is completely impractical for UI at 60fps. React’s team made two heuristics that bring this down to O(n):First, if two elements have different types (say div vs span, or Header vs Footer), React does not bother diffing their children. It destroys the entire old subtree and builds a new one. This sounds expensive, but in practice, type changes at the same tree position are rare.Second, the key prop lets developers hint at identity across renders so React can match children efficiently without deep comparison. The tradeoff is that developers must choose good keys. If you use Math.random() as a key, you have destroyed the entire benefit and React recreates every item every render — worse than no keys at all.The practical cost of these heuristics is rare false positives: if you swap a div wrapper for a section wrapper, React rebuilds the entire subtree even though the children are identical. But that cost is negligible compared to the O(n^3) alternative.Explain JSX transformation. If JSX is not valid JavaScript, how does it end up running in the browser? What changed in React 17?
Explain JSX transformation. If JSX is not valid JavaScript, how does it end up running in the browser? What changed in React 17?
React.createElement(type, props, ...children) call, which is why you had to import React from 'react' in every file that used JSX — even if you never referenced React directly. The import was invisible at the source level but required at runtime.React 17 introduced the “new JSX transform” (react/jsx-runtime). Instead of React.createElement, the compiler emits _jsx(type, props) and auto-imports it from react/jsx-runtime. Two practical benefits: you no longer need the React import boilerplate in every file, and the new runtime is slightly more efficient because it avoids the createElement overhead of always extracting key from props at runtime — _jsx handles keys as a separate argument.In terms of what the browser actually sees: <div className="box">Hello</div> becomes something like _jsx("div", { className: "box", children: "Hello" }). Nested elements become nested calls. Fragments become _jsxs(_Fragment, { children: [...] }). The returned objects are plain JavaScript objects describing the Virtual DOM node — they have a type, props, key, and ref. React’s renderer then walks this tree to produce actual DOM nodes.Follow-up: What happens if you use a lowercase component name in JSX like <myComponent />? Why?React treats lowercase JSX tags as native HTML elements and uppercase as React components. If you write <myComponent />, the compiler produces _jsx("myComponent", {}) — passing the string "myComponent" as the element type. The browser will try to render a <mycomponent> HTML element (an unknown element, essentially a generic inline element), not your React component. No error is thrown; it just silently renders the wrong thing, which makes it a tricky bug to diagnose. The fix is always capitalizing component names: <MyComponent /> compiles to _jsx(MyComponent, {}), passing the function reference.This is a compile-time convention, not a runtime check. The Babel/SWC plugin simply looks at the first character’s case to decide whether to emit a string or a variable reference. It is one of those decisions that is simple to implement but occasionally confusing for developers coming from frameworks where component naming is not case-sensitive.A teammate argues that the Virtual DOM is slower than direct DOM manipulation because it adds an extra layer. How do you respond?
A teammate argues that the Virtual DOM is slower than direct DOM manipulation because it adds an extra layer. How do you respond?
element.textContent = 'new value', that is always faster than building a Virtual DOM tree, diffing it, and then doing the same textContent assignment. The Virtual DOM adds CPU work that a surgical manual update does not need.But that argument misses the real problem the Virtual DOM solves: developer productivity and correctness at scale. In a real application with hundreds of components, state flowing through many layers, and UI that depends on multiple data sources, manually figuring out the minimal set of DOM operations for every state change is error-prone and maintenance-heavy. You end up with bugs where the DOM is out of sync with the data, or you over-update (causing layout thrashing), or you forget to clean up listeners.The Virtual DOM’s real value is that it lets you write declarative code — “given this state, render this UI” — and React figures out the efficient delta. In benchmarks, React’s batched updates often outperform naive imperative approaches because it coalesces multiple state changes into a single DOM write. For example, if three separate state updates happen in the same event handler, React batches them into one re-render and one DOM commit rather than three separate layout-triggering writes.That said, for extreme performance scenarios — canvas rendering, animations at 120fps, or very large virtualized lists — the Virtual DOM can become a bottleneck. That is why libraries like Svelte compile away the Virtual DOM entirely and generate direct DOM update instructions. And it is why React introduced concurrent features and the Fiber architecture: to make the reconciliation interruptible so that high-priority updates (user input) are not blocked by low-priority ones (background data).Follow-up: How does React Fiber change the reconciliation model compared to the original stack reconciler?The original stack reconciler processed the entire component tree synchronously in one call stack. If you had 1000 components to reconcile, the main thread was blocked until all 1000 were processed. During that time, the browser could not handle user input, animations froze, and the app felt janky.Fiber replaces the single recursive walk with an interruptible linked-list data structure. Each Fiber node represents a component and links to its child, sibling, and parent. React can pause work on one branch, handle a higher-priority update (like a keystroke), and resume where it left off. This is the foundation of concurrent rendering, startTransition, and Suspense.In practice, this means React can break a large reconciliation into small chunks spread across multiple animation frames, keeping the app responsive even during heavy updates. The tradeoff is increased internal complexity — the Fiber data structure and the two-phase commit (render phase vs commit phase) are considerably more complex than the old stack reconciler. But that complexity is internal to React; as a developer, you write the same declarative components.You are debugging a production React app where the UI does not update after a state change. What are the common causes and how do you systematically diagnose?
You are debugging a production React app where the UI does not update after a state change. What are the common causes and how do you systematically diagnose?
state.items.push(newItem); setState(state.items), React sees the same array reference and skips the re-render. The fix is always creating a new reference: setState([...state.items, newItem]). This is the single most common cause I have seen in production codebases, especially with nested objects where someone does state.user.name = 'new' instead of spreading.Second, stale closures. If a useEffect or event handler captures an old value of state because the dependency array is wrong, the handler runs with outdated data. The UI might update but the handler logic uses the old value. I check this by logging inside the handler and comparing with the React DevTools state.Third, the component is not actually subscribed to the state that changed. For Context, this means the component is not inside the Provider, or the custom hook is reading from a different context instance. For Redux, the selector might be returning a memoized value that has not changed despite the store updating.Fourth, React.memo with a custom comparator that is too aggressive — it returns true (props are equal) when they are actually different, so the component never re-renders.Fifth, the state update might be correct but the derived value displayed in the UI is computed incorrectly. I have seen cases where useMemo has a stale dependency array, so the memoized value is never recomputed.My systematic approach: React DevTools is step one. I check whether the state actually changed in the Components panel. If it did, I check whether the component re-rendered using the Profiler. If it re-rendered but the DOM did not update, the issue is in the JSX logic (conditional rendering, wrong variable). If it did not re-render, I trace upward to find where the re-render chain breaks — usually a memo boundary or a Context that is not updating.Follow-up: How would you detect and fix a stale closure bug in a useEffect that fires a setInterval?Classic scenario: you have an interval that reads count but the dependency array is empty, so the closure captures the initial count value forever.Detection: add a console.log(count) inside the interval callback and watch it always log the same value even after clicking increment. Or use the React DevTools Profiler to confirm the component re-renders with the correct state but the interval callback is stuck on an old value.Fix: use the functional updater form setCount(c => c + 1) instead of setCount(count + 1). The functional form does not read count from the closure — it receives the current value as an argument from React. Alternatively, store count in a ref and read countRef.current inside the interval, but the functional updater is cleaner for state updates. For cases where you need to read state without updating it (like logging), a ref is the right tool.