Study Index
React · Foundations Refresher

React, from the metal up

A running set of session notes — what React actually is, and the tooling that turns it into a production app. Basics kept brief, gotchas surfaced, interview Q&A in every section.

Session 01

The Inception

What React really is under the hood — elements, the root, rendering.

React is just JavaScript — a small library, not a framework. It hands you helper functions to build UI and update the DOM efficiently. Everything below is plain JS objects and function calls.

1.1

Two packages, one job each

React comes as two separate packages because the core doesn't care where it draws — the same core powers the browser, mobile (React Native), and more. Each environment just swaps in its own "renderer."

react
The core (the brain)

Creates elements and components and works out what changed. Knows nothing about the browser or DOM.

react-dom
The renderer (the hands)

Takes the core's output and puts it on the actual web page. The browser-specific half.

1.2

An element is an object, not HTML

The most important mental model. createElement does not create an <h1> — it returns a plain JS object describing one. Log it and you'll see type and props, never markup.

// type, props (attributes), children
const heading = React.createElement(
  "h1",
  { id: "title" },        // 2nd arg = props/attributes
  "Hello, world"        // 3rd arg = children
);
// => { type:"h1", props:{ id:"title", children:"Hello, world" } }
Arg 1 · type
The tag

"h1", "div"… or a component.

Arg 2 · props
Attributes

An object. Anything passed lands in props.

Arg 3 · children
Contents

String, one element, or an array. Also lives in props.children.

1.3

The root & render

You give React one DOM node to own. It renders inside that node and nowhere else.

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(heading);
  • render() replaces, it does not append. Whatever already lives inside the root is wiped on first render.
  • React only controls its root subtree. Markup above or below #root is untouched — which is what lets React drop into one widget or card.
  • createRoot lives in react-dom, createElement in react. Building UI vs. committing it to the DOM are separate concerns.
1.4

Nesting, siblings & why JSX exists

Nest by passing a child element as arg 3. Siblings = pass an array.

React.createElement("div", { id: "parent" }, [
  React.createElement("h1", {}, "Heading 1"),
  React.createElement("h2", {}, "Heading 2"),
]);

Two levels deep and it's already unreadable. That pain is the whole reason JSX exists — sugar that compiles straight back to these createElement calls. React never requires JSX.

1.5

Why it's fast · Library vs Framework

Changing the real page (the DOM) is the slowest thing a browser does. So React keeps a copy of the UI as plain JS objects (the Virtual DOM), works out the smallest set of changes needed, and only then touches the real page.

● Library

Bare-bones. Drop into a small portion of an existing app — even alongside jQuery. You choose the tooling.

vs
● Framework

Opinionated and all-in. Generally expects you to build the whole app its way, batteries included.

1.6

Gotchas & nice-to-knows

Gotcha
Import / script order

Dependencies load before code that uses them — react & react-dom before your app, or you hit React is not defined.

Gotcha
Sibling arrays need keys

Rendering a list of elements warns about a missing key. A key is a unique label that helps React tell the items apart between updates.

Nice to know
An element is immutable

Once created it's a frozen snapshot. To change UI you create new elements and render again — never mutate in place.

Curiosity
The secret internals

React exposes an object literally named __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. Real, intentionally unusable.

Analogy · recall

Library = a power tool you pick up and use where you want; framework = a factory assembly line you step into and follow. Or: with a library you call it; with a framework it calls you (inversion of control).

1.7

Interview Q&A

Q.Is React a library or a framework? Why does it matter?
A library. A library is a small tool you plug into your app wherever you want; a framework is the whole structure you build inside. Because React is just the view layer, you pick your own router, state tool, and bundler — React doesn't force them on you.
Q.What does React.createElement return?
A plain JavaScript object — called a React element. It just describes what to show (its type, props, and children). It is not an HTML tag yet; ReactDOM turns that object into real HTML later.
Q.What's the difference between react and react-dom?
react is the brain — it creates elements and figures out what changed. react-dom is the hands for the browser — it puts that onto the actual page. They're split because React also runs elsewhere (e.g. React Native uses a different "hands" package).
Q.Does render() append or replace?
It replaces. Whatever was already inside the root element gets wiped and swapped for what you render. React only touches that one root — the rest of the page is left alone.
Q.What is the Virtual DOM and why is it fast?
The Virtual DOM is a lightweight copy of the UI kept as JS objects in memory. When something changes, React compares the new copy with the old one (called diffing) and updates only the parts that actually changed. Touching the real DOM is slow, so doing less of it is the speed-up.
1.8

One-line recall

react = core react-dom = renderer element = JS object createElement(type, props, children) createRoot() + render() render replaces owns root subtree only array children → keys JSX → createElement virtual DOM + diffing library, not framework
Session 02

Igniting the App

The tooling that turns raw React into a fast, production-ready build.

React alone is not what makes an app fast. A bundler (here, Parcel) does the heavy lifting — bundling, minifying, caching, tree-shaking. create-react-app just hides all of this; here we wire it by hand.

2.1

npm — the package manager

npm is a giant online store of reusable code packages, plus the tool that installs and version-manages them in your project. ("Package" and "dependency" mean the same thing — code your project relies on.) Fun aside: npm isn't officially short for "Node Package Manager," though that's exactly what it does.

npm init
Adopt npm

Walks you through a few prompts and generates package.json. -y skips the questions.

npm install / npm i
Add a package

npm i is shorthand for npm install. Pulls code from the registry into node_modules.

2.2

The four artifacts npm creates

package.json
The shopping list

Your project's settings + the packages you want, with allowed version ranges. Commit it.

package-lock.json
The receipt

The exact version of every package actually installed (including their dependencies), with a hash to verify it. Commit it.

node_modules/
The actual code

The real downloaded code of every package and its dependencies. Huge. Never commit — rebuild it with npm install.

.gitignore
What stays out

Lists things git should skip: node_modules, /dist, .parcel-cache.

  • Golden rule: anything you can regenerate doesn't belong in git. Commit the recipe (package + lock), not the result (node_modules, dist, cache).
2.3

Version ranges: caret vs tilde

The symbol before a version in package.json controls auto-upgrades on the next install. Versions are MAJOR.MINOR.PATCH.

^ caret
Minor + patch

^2.8.3 → accepts 2.x.x, not 3.0.0. The safe default.

~ tilde
Patch only

~2.8.3 → accepts 2.8.x only. More conservative.

(none)
Exact pin

2.8.3 → never auto-upgrades. Total control.

  • Why caret is the default: minor/patch upgrades are meant to be backward-compatible; major bumps can break your app. The lock file is what guarantees everyone actually installs the same resolved version regardless of the range.
2.4

dev vs normal dependencies

● devDependencies — -D

Needed only while developing/building: bundlers, test runners. npm i -D parcel.

vs
● dependencies

Needed at runtime / in production: React itself. npm i react react-dom (no flag).

Transitive dependencies: your dependency has its own dependencies, which have theirs — a whole tree. Every package carries its own package.json. That tree is why node_modules balloons.

2.5

React via npm, not CDN

Swapping the CDN <script> tags for installed packages, and pulling them in with import.

import React from "react";
import ReactDOM from "react-dom/client";  // note: /client for createRoot
  • Why npm beats CDN: no extra network call (it's local in node_modules), and version management lives in package.json instead of a hard-coded URL you'd hand-edit on every upgrade.
  • The import resolves to node_modules. from "react" means "the react package in node_modules" — the same role the CDN global used to play.
2.6

npx & running Parcel

npm installs packages; npx executes one. Parcel is a zero-config bundler.

# dev: build + serve at localhost:1234 with HMR
npx parcel index.html

# production: optimized build into /dist
npx parcel build index.html
Entry

index.html is the source Parcel starts from.

Dev build

Output written to /dist, served on a local port.

HMR

Edits hot-reload the page instantly.

2.7

What Parcel actually does

Dev server + HMR
Hot Module Replacement

Runs a local server and, when you save, updates the page instantly — it watches your files for changes (the watcher is written in C++ for speed).

Caching
.parcel-cache

Remembers previous build work, so each rebuild after the first drops to a few milliseconds.

Bundling
Many files → few

Combines all your files into a handful (one HTML, one CSS, one JS) for production.

Minify + compress
Smaller payload

Removes whitespace, shortens variable names, and compresses output so it downloads faster.

Tree shaking
Removes unused code

If you import a library but use only part of it, the unused parts are dropped from the final bundle.

And more
Images, splitting, HTTPS

Optimizes images, splits big bundles into smaller chunks, shows friendly error messages, can serve over HTTPS.

2.8

Browserslist & differential bundling

In package.json you list which browsers your app must support. Parcel and Babel read this and add only the extra compatibility code those browsers need.

"browserslist": [
  "last 2 chrome versions",
  "last 2 firefox versions"
]
  • It's a promise, not a blocklist. The browsers you list are guaranteed to work; others probably still work, just not promised. Fewer targets = lighter, faster bundle.
  • Differential bundling = Parcel makes a modern build for new browsers and an older-compatible build for old ones, and each browser loads the right one. A bank/government site might aim for ~99% browser coverage; an internal dev tool can settle for ~80%.
2.9

Gotchas & nice-to-knows

Gotcha
"Browser scripts can't have imports"

Add type="module" to the <script> so the browser treats app.js as an ES module.

Gotcha
Remove "main" for parcel build

The auto-generated "main": "app.js" in package.json conflicts with Parcel's HTML entry — delete it.

Gotcha
react-dom/client

Import createRoot from react-dom/client, not react-dom, or you get a warning.

Nice to know
Local ≠ server node_modules

Server runs its own npm install from the lock file — the integrity hash guarantees identical versions ("works on my machine" fix).

Nice to know
Two build flavors

Dev build is fast + light on optimization; production build is slower but minified, tree-shaken, hashed.

Curiosity
Parcel is a manager too

It doesn't do everything itself — it orchestrates helper libs (Babel, etc.), the same way it has its own transitive deps.

Analogy · recall

package.json = the shopping list (what + acceptable versions). package-lock.json = the receipt (exact items + barcodes you actually bought). node_modules = the pantry full of the actual groceries — you never ship the pantry, you re-shop from the list with npm install.

2.10

Interview Q&A

Q.package.json vs package-lock.json?
package.json is your shopping list — it says which packages you want and roughly which versions (e.g. ^2.8.3). package-lock.json is the receipt — it records the exact version of every package that got installed, plus a hash to verify it. The lock file makes sure your machine and the server install the same thing.
Q.Should you commit node_modules? Why not?
No. It's huge and you can always rebuild it. You commit the list (package.json + lock file), and npm install recreates node_modules anywhere. Simple rule: if you can regenerate it, don't put it in git — same for /dist and .parcel-cache.
Q.What do ^ (caret) and ~ (tilde) mean?
Versions read as MAJOR.MINOR.PATCH. Caret ^2.8.3 = auto-update minor and patch (stays below 3.0.0). Tilde ~2.8.3 = auto-update patch only (stays below 2.9.0). Caret is the safe default: minor/patch are meant to be safe, while a major version can break things.
Q.dependencies vs devDependencies?
dependencies are needed when the app runs (React itself). devDependencies (npm i -D) are only needed while building (Parcel, test tools, linters) and aren't shipped to users. It's mainly about keeping "build-time" and "run-time" tools separate.
Q.npm vs npx?
npm installs a package; npx runs one. So npm i -D parcel downloads Parcel, and npx parcel index.html executes it.
Q."My React app is fast" — what's actually making it fast?
Not React by itself. The bundler (Parcel/Webpack/Vite) does the heavy lifting — it minifies, bundles, removes unused code (tree shaking), compresses, optimizes images, and caches. React helps at runtime, but the speed mostly comes from the build tools.
Q.What is a transitive dependency?
A dependency of your dependency. You install Parcel, Parcel needs Babel, Babel needs other packages — it chains into a whole tree. That's why installing one package can pull in thousands of files.
2.11

One-line recall

npm i = install npx = execute package.json = ranges lock = exact + hash node_modules = regenerate ^ minor · ~ patch -D = devDependency transitive deps tree type="module" parcel: bundle/minify/treeshake HMR + cache /dist = output browserslist = targets commit recipe, not result
Session 03

Laying the Foundation

JSX, Babel, and components — the syntax you'll actually write every day.

JSX is not React and not HTML — it's an HTML-like syntax that Babel transpiles into React.createElement calls. A component is just a function that returns JSX.

3.1

npm scripts

Stop typing npx parcel by hand. Define scripts in package.json — the industry-standard way to start/build any project. Lost in a new codebase? Read its scripts to learn how to run it.

"scripts": {
  "start": "parcel index.html",        // dev
  "build": "parcel build index.html"   // prod
}
  • Run with npm run <name>. e.g. npm run start, npm run build.
  • start is special. npm start works as a shortcut for npm run start; other scripts need the explicit run (npm build won't work).
3.2

JSX is not HTML (and not React)

Creating elements with React.createElement is clumsy and unreadable when nested. JSX is the developer-friendly syntax Facebook built to fix that. Both produce the identical React element object — console-log them and they're the same.

// JSX
const heading = <h1 id="title">Namaste React</h1>;

// ...is exactly the same React element as:
const heading = React.createElement("h1", { id: "title" }, "Namaste React");
  • It only looks like HTML. The browser can't actually run JSX — it understands plain JavaScript only. A build step converts JSX first (next section).
  • Write code for humans first, machines second. That's the whole point of JSX — it's far easier to read than nested createElement calls.
3.3

JSX differs from HTML

className
not "class"

Use className instead of class for CSS classes.

camelCase attrs
tabIndex, onClick

Attributes are camelCased: tabIndex, not tabindex.

multiline = ( )
Wrap in parens

Multi-line JSX must be wrapped in ( ) so Babel knows where it starts/ends.

3.4

Babel & the transpile pipeline

JSX works because Babel (a JS compiler/transpiler that Parcel installs and orchestrates) converts it before it ever reaches the JS engine.

You write

JSX

Babel

React.createElement

React

→ React element (object)

ReactDOM

→ HTML on the DOM

  • Babel does more than JSX. It also rewrites modern JavaScript into older syntax so it runs in older browsers (using your Browserslist from Session 2). Try it live at babeljs.io → "Try it out."
  • How it converts: Babel reads your code piece by piece, builds a structured map of it (an AST, or abstract syntax tree), then writes out the equivalent plain JavaScript. You don't need to know the internals — just that it's a reliable translator.
3.5

Components: functional vs class

A functional component is just a JS function that returns JSX. That's the entire definition. Everything on a page — button, header, card, list — is a component.

const Title = () => (
  <h1 className="head">Namaste React</h1>
);
● Functional — modern

A function returning JSX. The current standard; ~99.99% of new code. Use this.

vs
● Class-based — legacy

Uses JS classes. Old style, still found in legacy codebases & some interviews.

3.6

Rendering & composition

  • Component names must be Capitalized. Lowercase is treated as a DOM tag — capital tells Babel/React it's a component.
  • Render a component as <Title />, not Title. An element renders as {element}; a component renders as a tag.
  • Component composition = putting a component inside another (<Title /> inside Heading). That's the fancy interview term for the obvious thing.
  • <Title />, <Title></Title>, and {Title()} all work — the last because a component is, at the end of the day, just a function you can call.
3.7

JavaScript inside JSX

The superpower: inside JSX, { } runs any JavaScript expression — variables, math, function calls, even another React element or component.

const data = 10000;
const Heading = () => (
  <div>
    {data}              // variable
    <h2>{100 + 200}</h2>  // expression => 300
    {title}             // a React element
    {Title()}           // or call the component
  </div>
);
3.8

Gotchas & nice-to-knows

Nice to know
JSX sanitizes → blocks XSS

Data inside { } is escaped/sanitized before render, so malicious API data can't run as a cross-site scripting attack. Free protection.

Gotcha
It's still JavaScript order

const can't be used before initialization. Composing two components into each other = infinite loop that freezes the tab.

Gotcha
Functions need explicit return

Arrow with ( ) implicitly returns JSX; an arrow/normal function with { } body needs an explicit return.

Nice to know
Normal functions work too

Arrow functions are the convention, but function Title(){ return ... } is equally valid.

Tooling
Prettier + helpers

Prettier auto-formats on save; Bracket Pair Colorizer, ESLint, Better Comments ease development.

Convention
"not rendered" fallback

Some put a placeholder in the root; if React fails to render you'll see it — a quick render-failure signal.

Analogy · recall

JSX is a translator's draft. You write in a human-friendly language (JSX); Babel is the translator that converts it to the "official" language the engine accepts (React.createElement). The engine never sees your draft — only the translation. And a component is a recipe (function); rendering <Title/> is cooking the dish (calling it).

3.9

Interview Q&A

Q.Is JSX HTML? Is it part of React?
No to both. JSX just looks like HTML, but it's a special syntax that browsers can't read directly. And it isn't part of React — React works fine without it using React.createElement. JSX is simply a nicer, more readable way to write that same thing.
Q.How does JSX work if browsers can't read it?
Before your code reaches the browser, Babel converts the JSX into plain React.createElement calls — which the browser does understand. So by the time the JS engine sees it, the JSX is already gone. Parcel runs Babel for you behind the scenes.
Q.What is Babel and what else does it do?
Babel is a tool that converts one kind of JavaScript into another. Here it turns JSX into React code. It also rewrites modern JavaScript into older syntax so it runs in older browsers. Think of it as a translator for your code.
Q.React element vs component?
An element is a single piece of UI (a plain object that JSX or createElement produces). A component is a function that returns elements. Element = the thing; component = the function that makes it. You use a component as <Comp /> and an element as {element}.
Q.Why must component names be capitalized?
It's how React tells the two apart. A lowercase tag (<div>) means a normal HTML element; a Capitalized tag (<Header>) means "this is my component — call that function." Lowercase your component and React looks for an HTML tag by that name instead.
Q.How does JSX protect against XSS?
Anything you put inside { } is treated as plain text, not code. So if bad data tries to sneak in a <script>, React shows it as harmless text instead of running it. You get this protection for free. (dangerouslySetInnerHTML turns it off — the scary name is the warning.)
Q.Functional vs class components — which and why?
Use functional components — they're shorter and, with Hooks, do everything classes used to (state, lifecycle). Class components are the older style; you'll still see them in old code, but you won't write new ones.
3.10

One-line recall

npm scripts: start / build npm start = npm run start JSX ≠ HTML ≠ React Babel: JSX → createElement className not class camelCase attrs multiline → ( ) component = fn returning JSX Capitalize names functional > class composition = nesting { } = any JS expression JSX sanitizes (anti-XSS) everything is JavaScript
Session 04

Talk is Cheap, Show the Code

Plan it, compose components, then make them dynamic with props, map & keys.

Build UI by composing reusable components and feeding them data via props. Render lists with .map() + a stable key, and let a config (data) drive what the UI shows.

4.1

Plan before you code

Senior move: design the UI before writing any JSX. Sketch a wireframe/mock, then derive the component tree from it. Good planning makes the code almost write itself.

Header

Logo + Nav items (Home, Cart…)

Body

Search + RestaurantContainer → RestaurantCard ×N

Footer

Copyright, links, contact

  • Two levels of planning: first the visual layout, then break it into a component hierarchy. Naming the components up front is half the work.
4.2

Composing components

Each piece is a functional component; the app is just components nested inside components (composition).

const AppLayout = () => (
  <div className="app">
    <Header />
    <Body />
    <Footer />
  </div>
);
root.render(<AppLayout />);
  • Reusability is the reason to extract a component. The moment you'd repeat markup (e.g. many restaurant cards), make it its own component — same logic as extracting a function.
4.3

Three ways to style

External CSS
className

A separate .css file + className. The default here.

Inline styles
style = JS object

style={{ }} — a JS object with camelCased properties. Not preferred.

Utility / libs
Tailwind, MUI…

Utility classes or component libraries (covered later).

// inline = JS object, hence the double braces
<div style={{ backgroundColor: "#f0f0f0" }}></div>
  • Double braces explained: outer { } = "JS goes here", inner { } = the object literal. Properties are camelCase (backgroundColor, not background-color).
4.4

Props — passing data to components

Props (properties) are just the arguments you pass to a component function. React collects everything you pass into a single props object.

// pass props (like function arguments)
<RestaurantCard resName="KFC" cuisine="Burgers" />

// receive them — two equivalent styles
const RestaurantCard = (props) => ...props.resName...
const RestaurantCard = ({ resName, cuisine }) => ...  // destructure on the fly
  • Destructuring on the fly ({ resName }) is plain JavaScript object destructuring — not React magic.
  • Pass whole objects, not 10 separate props. <Card resData={obj} /> keeps the call clean; read it as props.resData. Note the { } — you're passing a JS object, not a string.
4.5

Config-driven UI

Big idea: you build the components once, and the data decides what shows up. Swiggy sends different offers and cards for Delhi vs. Mumbai — same app, different data, different page. If a section has no data, it simply doesn't appear. The data that controls this is the "config."

Analogy · recall

Config-driven UI = a stage that follows a script. The stage (your components) is built once; the script (JSON config) decides which actors appear, in what order, tonight in Delhi vs. Mumbai. Change the script, not the stage.

  • UI layer + data layer. A strong front-end engineer owns both — you should be able to question an API's shape, not treat data as "the backend's problem."
  • Real data is messy & deeply nested (e.g. resData.data.name). Use optional chaining (?.) to read it safely, and watch for redundant/duplicated fields in real APIs.
4.6

Rendering lists with map()

Don't hand-write N cards. Loop the data array with .map() inside JSX and return a component per item.

<div className="res-container">
  {resList.map((restaurant) => (
    <RestaurantCard key={restaurant.data.id} resData={restaurant} />
  ))}
</div>
  • Prefer map/filter/reduce over for loops — functional style is the React norm and reads cleaner inside JSX.
  • Build display strings in JSX, not in the API. e.g. {cuisines.join(", ")} or {"₹" + costForTwo/100 + " for two"}.
4.7

Keys — and why index is an anti-pattern

Every item you render in a .map() needs a unique key. It's not decoration — the key is how React identifies each item, so when the list changes it can update just the one that changed instead of redrawing all of them.

● Unique id (best)

key={item.id}. React updates only what changed; inserting one item re-renders one card, not the whole list.

vs
● Index (last resort)

key={index}. Works, but if the list reorders/inserts, indexes shift and React mis-associates items — bugs + wasted renders.

  • Priority: unique id > index > no key. No key is unacceptable; index is a fallback only when no id exists (ask the backend for one).
4.8

Gotchas & nice-to-knows

Gotcha
className, not class

Easy slip coming from HTML — React warns "Invalid DOM property `class`." Use className.

Gotcha
Numbers, not NaN children

Reading a wrong path returns undefined → "Received NaN for children." Verify the data path (resData.data.x).

Nice to know
Images live on a CDN

APIs send an image id; build the URL as {CDN_URL + imageId} (string concat inside { }).

Nice to know
Optional chaining

resData?.data?.name avoids crashes when an intermediate value is missing.

Gotcha
Whole object vs string prop

resData={obj} passes the object; resData="obj" passes the literal string "obj".

Practice
Destructure to clean up

Pull fields once at the top: const { name, cuisines } = resData.data; — beats repeating resData.data. everywhere.

4.9

Interview Q&A

Q.What are props?
Props are the data you pass into a component — just like passing arguments to a function. React gathers everything you pass into one props object. They're read-only: a component uses its props but never changes them. Data flows one way, parent → child.
Q.Why does React need a key when rendering lists?
A key gives each item a unique name so React can tell them apart. When the list changes, React updates only the item that changed instead of re-drawing the whole list. Without keys it can't tell what's new, so it does extra work and can show bugs.
Q.Why is using array index as a key an anti-pattern?
The index isn't tied to the item — if the list reorders or you add something at the top, index 0 now points to a different item. React gets confused about which is which, leading to wrong updates. Only safe for a list that never changes order and has no id.
Q.What is config-driven UI?
It means the backend data decides what the UI shows, instead of hard-coding it. You build the components once, and the data (config) controls which cards/offers appear — so the same app shows different things in Delhi vs Mumbai. If the data for a section is missing, that section just doesn't appear.
Q.How do you style components in React?
Three common ways: a separate CSS file with className, inline styles using a JS object (style={{ }}), or a library like Tailwind or styled-components. CSS files are the usual choice; inline is fine for small one-offs.
Q.What is destructuring props "on the fly"?
Pulling the values you need straight out of props in the function's parameters: ({ resName, cuisine }) => … instead of writing props.resName every time. It's just normal JavaScript — same result, cleaner to read.
Q.Can you pass an object as a single prop?
Yes — <Card resData={obj} />. It's cleaner than passing ten separate props. You then read it as props.resData. The curly braces matter: they pass the real object, whereas quotes would pass the text "obj".
4.10

One-line recall

plan → wireframe → tree compose components extract = reusable props = fn arguments props read-only destructure on the fly pass whole object 3 ways: css / inline / utility inline = JS object {{ }} config-driven UI UI layer + data layer optional chaining ?. .map() for lists key = unique id index key = anti-pattern
Session 05

Let's Get Hooked

State, re-rendering, and the engine (Virtual DOM) that makes React fast.

A normal variable can't update the screen. A state variable (made with useState) can — because whenever state changes, React re-renders the component and syncs the UI to the data.

5.1

Why React (when plain HTML/JS can do it)?

Honest truth: anything React does, you could do with plain HTML/CSS/JS — React is JavaScript under the hood. The point of a library is developer experience: write less code, build more, and let React handle the hard part — keeping the screen in sync with your data, fast.

5.2

Project structure: one component per file

A 2,000-line App.js is unreadable. Split each component into its own file. The common convention: a src/ folder for source code, a components/ folder inside it, and a utils/ folder for shared things.

src/
All source code

Convention, not a rule. Keeps the project root clean.

components/
One file per component

Header.js holds the Header component — file name matches the component, Capitalized.

utils/
Shared bits

Constants (constants.js), mock data, helpers — lowercase names, since they aren't components.

  • Never hard-code data inside a component file. Move constants (CDN URL, logo) and mock data into utils/ so they're reusable and the component stays clean.
  • Keep files small — a rough industry habit is to break a component up once it grows past ~100–200 lines. React itself has no opinion on folder structure; pick something simple and refactor as you grow.
  • .js vs .jsx doesn't matter. Either works; just be consistent. Don't overthink it.
5.3

Import / export: default vs named

To use a component across files, you export it from its file, then import it where you need it (give before you take). Two flavors:

// DEFAULT — one per file
export default Header;
import Header from "./components/Header";

// NAMED — as many as you want, names must match
export const CDN_URL = "...";
import { CDN_URL } from "../utils/constants";  // note the { }
  • Default = the file's main thing (usually a component); import it with any name, no braces. Named = pick specific things by exact name, wrapped in { }. A file can have one default but many named exports.
  • The file extension is optional on import. "./components/Header" resolves to the .js file automatically.
5.4

Event handlers

Make the UI interactive by passing a function to events like onClick. The function runs when the event fires.

<button onClick={() => {
  console.log("clicked");
}}>Top Rated</button>
  • Pass the function, don't call it. onClick={handleClick} — React calls it on click. Every DOM event has a React equivalent (onClick, onMouseOver, onChange…), all camelCased.
5.5

The core problem: a normal variable can't update the UI

Say a button filters a list. You can write the filter in plain JS and the data changes — but the screen doesn't budge. A normal let variable has no way to tell React "I changed, redraw me." That gap is exactly what React's state solves.

Analogy · recall

A normal variable is a whiteboard in a locked room — you can change it, but no one outside sees. A state variable is a live scoreboard — change the number and the display updates automatically for everyone watching.

5.6

useState — the first hook

A hook is just a JavaScript function React gives you with a built-in superpower. useState creates a state variable: a value React watches, plus a setter function to change it.

import { useState } from "react";   // named import

// [ current value, function to update it ]
const [listOfRes, setListOfRes] = useState(resList);  // arg = initial value
  • The two parts: listOfRes is the current value; setListOfRes is the only correct way to change it. The argument to useState is the starting value.
  • Never reassign state directly. listOfRes = [...] does nothing useful — you must call setListOfRes(newValue), which is what tells React to re-render.
  • State is local to its component — scoped to that function, like any local variable.
5.7

The one rule: state change → re-render

This is the heart of React: whenever a state variable changes, React re-renders that component and updates the screen to match. Call the setter, and the UI follows automatically — you never touch the DOM yourself.

1 · Event

User clicks a button

2 · setState

You call the setter with new data

3 · Re-render

React re-runs the component

4 · UI synced

Screen matches the new state

5.8

Under the hood: Virtual DOM & reconciliation

When people say "React is fast," they mean it's efficient at updating the DOM — the slow part of any web page. Here's how.

Virtual DOM
A JS copy of the UI

A lightweight object representing the real DOM. (A React element is exactly this kind of object.)

Diffing
Old vs new copy

On a state change React builds a new Virtual DOM and compares it to the old one — like a "git diff" between two objects.

Reconciliation
Update only the diff

React then changes only the real DOM nodes that actually differ, leaving the rest untouched.

  • Why this is fast: comparing two plain JS objects is cheap; rewriting real HTML is expensive. React diffs the cheap copies and touches the real DOM as little as possible.
  • React Fiber is the name of React's current reconciliation engine (rewritten in React 16). It enables splitting rendering work into chunks. Interview-ready phrasing: "React does efficient DOM manipulation via a Virtual DOM and a diffing algorithm."
5.9

Gotchas & nice-to-knows

Gotcha
Import useState from react

It's a named import: import { useState } from "react" — braces required.

Gotcha
The setter, not =

Updating state with listOfRes = x won't re-render. Use setListOfRes(x).

Nice to know
The [ ] is destructuring

const [a, setA] = useState()useState returns an array; you're just array-destructuring it. Plain JS, no magic.

Convention
Name the setter set+Name

For listOfRes, call it setListOfRes. Not enforced, but everyone follows it.

Gotcha
Duplicate keys when copying

Reused mock data can give two cards the same key — React warns. Give each a unique id.

Nice to know
Virtual DOM predates React

The idea (an object representation of the DOM) existed before; React popularized it and built its diffing algorithm on top.

5.10

Interview Q&A

Q.What is a React hook?
A hook is a function React gives you that adds a built-in capability to a functional component — like state or side effects. The two you'll use most are useState and useEffect. They're just normal JS functions with logic written inside React.
Q.What does useState do?
It creates a state variable — a value React keeps an eye on — and gives you a function to update it. It returns a pair: [value, setValue]. The argument you pass is the starting value.
Q.Why can't I just update a normal variable to change the UI?
A normal variable has no link to the screen — changing it doesn't tell React to redraw. A state variable does: when you update it via its setter, React re-renders the component and the UI updates. That link is the whole point of state.
Q.What happens when state changes?
React re-renders the component — it re-runs the function, builds a new Virtual DOM, compares it to the old one, and updates only the parts of the real page that changed. So the UI always stays in sync with the data.
Q.What is the Virtual DOM?
A lightweight JavaScript object that represents the real DOM. React keeps one in memory; updating it is cheap. It's the same kind of object a React element is.
Q.What is reconciliation / React Fiber?
Reconciliation is React's process of finding the difference between the old and new Virtual DOM (diffing) and applying only those changes to the real DOM. React Fiber is the name of the engine that does this, rewritten in React 16 to be more efficient.
Q."Why is React fast?" (the interview line)
Because it does efficient DOM manipulation: it keeps a Virtual DOM, diffs the old vs new versions (cheap object comparison), and updates only the real DOM nodes that changed instead of redrawing everything. Note: React doesn't minify or bundle — that's the build tools (Session 2).
5.11

One-line recall

why React = DX, sync UI to data one component per file src / components / utils no hard-coded data in components export default = one named export = many { } onClick={fn} normal var ≠ UI update useState = state var [value, setValue] use the setter, not = state change → re-render virtual DOM = JS object diffing + reconciliation React Fiber (v16)
Session 06

Exploring the World

Fetching live data with useEffect, plus shimmer, conditional & controlled inputs.

Render the page first, then fetch. useEffect runs your API call after render; you drop the data into state, and React re-renders with the real content.

6.1

Monolith vs microservices

Two ways to structure a whole application's backend — useful context for where a React app fits.

● Monolith

One big project holds everything — UI, APIs, auth, DB, notifications. Change one button and you rebuild/redeploy the whole thing.

vs
● Microservices

Separate small services, each with one job, its own tech stack, and its own deploy cycle. They talk to each other over the network.

  • Separation of concerns / single responsibility — each service does one thing. Your React app is just the UI microservice; it calls a backend service to get data.
  • Services connect by URL/port — e.g. UI on :1234, backend mapped to /api. Mix stacks freely (React UI, Java backend, etc.).
6.2

When to fetch: two approaches

When should the API call happen relative to rendering? React favors the second option.

● Load → fetch → render

Wait for the data, then show the page. User stares at a blank/frozen screen until it arrives.

vs
● Render → fetch → re-render (React way)

Show the page (skeleton) instantly, fetch in the background, then re-render with data. Better UX.

  • Two renders is fine. React's render cycle is fast and cheap, so rendering twice for a smoother experience is a good trade.
6.3

useEffect — run code after render

useEffect takes two arguments: a callback function and a dependency array. The callback runs after the component finishes rendering — the perfect place for an API call.

import { useEffect } from "react";

useEffect(() => {
  fetchData();      // runs AFTER render
}, []);             // dependency array
  • Order: the component renders first, then the effect's callback fires. (Log inside both and "body rendered" prints before "useEffect called.")
  • The empty [] means "run once" — after the first render only. (More on dependencies in a later session.)
6.4

Fetching data: fetch + async/await

Fetching in React is plain JavaScript — fetch() is a browser superpower, not a React one. Resolve its promise with async/await (cleaner than .then() chains).

const fetchData = async () => {
  const data = await fetch(SWIGGY_URL);
  const json = await data.json();
  setListOfRes(json?.data?.cards…);  // drop into state → re-render
};
  • Update state with the result. Calling the setter re-renders the component with live data — the same state-driven flow shown earlier.
  • Real APIs are deeply nested & messy — you dig through paths like json.data.cards[2].data.data.cards. Use optional chaining (?.) to read it safely.
6.5

CORS — why the call gets blocked (and how to fix it)

Calling another site's API from localhost often fails with a CORS error. It's the browser blocking requests from one origin to a different origin — a security policy, not a React bug.

Analogy · recall

CORS is a bouncer at the API's door. The server decides which origins are on the guest list. Your localhost isn't on Swiggy's list, so the browser's bouncer turns the request away.

Dev only
CORS browser extension

Toggles the check off in your browser. Fine for local learning — but your users won't have it, so it can't ship.

Works for everyone
A CORS proxy

Route the call through a proxy (e.g. corsproxy.io) by prefixing your API URL. The proxy fetches from Swiggy server-side and returns it — no browser block.

  • Why the proxy works: the browser only blocks browser cross-origin calls. A server has no such limit, so the proxy calls the API for you and hands back the data.
  • Real production fix: the API's own server enables CORS for your origin, or your own backend proxies the request. Public proxies are rate-limited (~tens of calls/min) — fine for dev, not for scale.
6.6

Shimmer UI & conditional rendering

While the API loads, don't show a blank page or a plain spinner — show a shimmer UI: fake skeleton cards that resemble the real layout. It's the modern standard (Swiggy, YouTube) and feels faster.

// conditional rendering with a ternary
return listOfRes.length === 0
  ? <Shimmer />
  : <div className="res-container">…</div>;
  • Conditional rendering = rendering different JSX based on a condition. The if/else or a ternary (? :) is just JavaScript — the fancy interview name is the only new part.
  • Empty list = data not here yetlistOfRes.length === 0 is the signal to show the shimmer.
6.7

Controlled input: why typing "does nothing"

A search box bound to state via value={searchText} is a controlled component — React owns its value. If you only set value and never update the state, typing appears frozen, because the box always shows the (empty) state.

<input
  value={searchText}
  onChange={(e) => setSearchText(e.target.value)}
/>
  • The fix is onChange: on every keystroke, read e.target.value and update state. State → re-render → the box shows the new value. That two-way wiring is what makes a controlled input work.
  • Every keystroke re-renders the component — and that's fine. React's diffing means only the input's value actually changes in the real DOM, so it stays fast even after dozens of renders.
6.8

Search & the "filter the original" trap

On search, filter the data and update state — React re-renders with the matches. But filtering and overwriting your one list destroys the full data, so the next search has nothing to search.

  • Keep two state variables: the full listOfRes (never mutated after fetch) and a filteredRes you render. Filter from the full list into the filtered one — so every search starts from all results.
  • Make search case-insensitive — lowercase both sides and use .includes() (not ===) so "coffee" matches "Third Wave Coffee."
6.9

When the live API changes — find it in DevTools

Live APIs change over time. The durable skill isn't memorizing one data path — it's reading the API off the real site instead of guessing when it breaks or you don't know its URL.

1 · Inspect

Open DevTools → Network tab

2 · Fetch/XHR

Filter to API calls only

3 · Refresh

Find the call returning the data

4 · Copy URL

Use it in your fetch

  • Install a JSON viewer extension to read big responses; open the API URL in a tab and use it to explore/search the structure.
6.10

Trace the nested JSON

The response is one huge config-driven blob — offers, "what's on your mind," filters, and (somewhere) the restaurant list. Walk the tree to the part you actually need.

// the restaurants live deep inside; the exact path shifts over time
const restaurants =
  json?.data?.cards[5]?.card?.card
    ?.gridElements?.infoWithStyle?.restaurants;
  • Config-driven UI, revisited: each top-level card builds one section (offers, filters, headings). You only pluck the card holding restaurants — which card index that is can change.
  • Always use optional chaining (?.) so a missing intermediate doesn't crash the whole app.
6.11

Re-map the renamed fields

When the shape changes, the fix is small — update the keys your card reads. In this change, the per-restaurant wrapper key data became info, and a couple of fields moved/changed type.

data → info
Wrapper renamed

restaurant.data.idrestaurant.info.id (same for name, image, rating…).

costForTwo
Number → string

It's now a ready string ("₹350 for two") — stop dividing by 100, just print it.

deliveryTime
Moved & renamed

Now info.sla.slaString (e.g. "22 mins") instead of a top-level deliveryTime.

  • Debug by reading the error + logging the object. "Cannot read properties of undefined (id)" points straight at the renamed path; log the restaurant to see the real keys.
6.12

Lazy loading via POST

The first call returns only ~9 restaurants. Scrolling triggers a separate POST API (passing lat/long) to load more — this is lazy loading / pagination.

  • GET vs POST: a GET puts params in the URL; a POST sends a body. fetch(url, { method: "POST", body … }) for the "load more" call.
6.13

Gotchas & nice-to-knows

Gotcha
CORS blocks cross-origin calls

The browser blocks localhost → other-site API. A dev extension unblocks it locally; for everyone, route through a CORS proxy (or fix it server-side).

Gotcha
Frozen input box

Setting value without onChange makes typing do nothing — it's a controlled input with no way to update.

Gotcha
Search === vs includes

Use .includes() for partial matches and lowercase both sides for case-insensitivity.

Gotcha
Don't overwrite the source list

Filtering into the same list loses your data. Keep a separate filtered list.

Gotcha
Renamed API keys crash render

"Cannot read properties of undefined" usually means a field moved (e.g. datainfo). Log the object and re-map the path.

Nice to know
Shimmer > spinner

Skeleton cards set expectations and feel faster than a lone spinner.

Nice to know
Two renders on load

The page renders once empty, then again after the fetch updates state — expected, not a bug.

Nice to know
APIs change — that's normal

Backend teams reshape responses; frontend adapts. Find the call in DevTools and trace the new JSON.

6.14

Interview Q&A

Q.What is useEffect and when does it run?
It's a hook for running code after the component renders — like fetching data. It takes a callback and a dependency array. With an empty array [], the callback runs once, right after the first render.
Q.How do you fetch data in React, and where?
Use plain fetch() (a browser feature) with async/await, inside a useEffect so it runs after render. Put the result into state with the setter — that triggers a re-render showing the live data.
Q.What is CORS?
A browser security rule that blocks requests from one origin (your site) to a different origin (another site's API) unless that server allows it. It's enforced by the browser, so the fix is on the server (allow the origin), via your own backend, or through a CORS proxy — not in your React code.
Q.How do you get past CORS without a browser extension?
Use a CORS proxy (e.g. corsproxy.io): prefix your API URL with the proxy's. The browser only blocks browser cross-origin calls, so the proxy — a server — fetches the API and returns the data. Unlike an extension, this works for all your users. Public proxies are rate-limited, so production should use the API's own CORS config or your own backend proxy.
Q.What is a controlled component?
An input whose value is driven by React state (value={state}) and updated via onChange. React is the single source of truth for what's in the box — you read and set it through state.
Q.What is conditional rendering?
Showing different UI based on a condition — e.g. a shimmer while loading, real cards when data arrives. You do it with an if, a ternary (? :), or &&. It's just JavaScript deciding what JSX to return.
Q.What is shimmer UI and why use it?
Placeholder skeleton cards shown while data loads. They mirror the real layout so the user sees something instantly and can anticipate the content — a smoother experience than a blank screen or a spinner.
Q.Why does the component re-render on every keystroke?
Because each keystroke updates a state variable (the search text), and any state change re-renders the component. It stays fast because React's diffing updates only the input's value in the real DOM, not the whole page.
Q.An API you depend on changed its response. How do you fix it?
Open DevTools → Network → Fetch/XHR, find the call and its response, and trace the new JSON path to the data you need. Then update your extraction code (and any renamed field keys), using optional chaining so missing values don't crash. It's normal maintenance — backend teams change APIs, frontend adapts.
Q.Why use optional chaining when reading API data?
Deeply nested responses may be missing an intermediate object. a?.b?.c returns undefined instead of throwing if b is missing — so one absent field doesn't break the whole render.
Q.GET vs POST for fetching?
GET requests data and carries parameters in the URL; POST sends data in a request body (used here to pass location for "load more"). In fetch, POST needs an options object with method and body.
Q.What is lazy loading / pagination?
Loading data in chunks instead of all at once — e.g. fetch the first page of restaurants, then fetch more as the user scrolls. It speeds up the initial load and saves bandwidth.
6.15

One-line recall

monolith vs microservices UI = a microservice render → fetch → re-render useEffect(cb, []) runs after render [] = run once fetch + async/await fetch = browser API optional chaining ?. CORS = browser blocks cross-origin CORS proxy works for everyone shimmer > spinner conditional rendering (ternary) controlled input: value + onChange keep full + filtered lists includes() + lowercase live APIs change — adapt DevTools → Network → Fetch/XHR trace nested JSON data → info (renamed key) costForTwo = string now deliveryTime → sla.slaString read error + log object lazy load via POST