In short
React is an open-source JavaScript library (created at Facebook, now Meta, released 2013) for building user interfaces as a tree of reusable components whose output is a pure function of their props and state. Instead of hand-editing the DOM, the developer declares what the screen should look like for a given state, and React computes the minimum set of real DOM changes using a virtual DOM and a reconciliation algorithm. It is the dominant front-end skill demanded in Indian hiring — TCS, Infosys, Wipro, Cognizant, Accenture, HCLTech, LTIMindtree, Zoho, Freshworks, Zomato, Swiggy, Flipkart, PhonePe and almost every product startup list React in fresher JD's — and appears in university syllabi as "Full Stack Development" / "Web Technologies" electives under the AICTE model curriculum. GATE CS does not examine React directly, but university end-semester papers, lab vivas and placement coding rounds do. A fresher is typically asked to explain the virtual DOM, write a counter or to-do list with hooks, and describe how data flows between components.
Explained
Why React: declarative UI and the virtual DOM
- Imperative DOM (plain JavaScript/jQuery): the programmer writes the *steps* —
document.getElementById('x').innerHTML = .... State lives in the DOM, so large apps drift out of sync and bugs multiply. - Declarative (React): the programmer writes a *description* — "for this state, the UI looks like this" — and React performs the steps. UI = f(state).
- Virtual DOM (VDOM): a lightweight JavaScript object tree mirroring the real DOM. On a state change React builds a new VDOM tree, diffs it against the previous one, and applies only the differences to the real DOM (reconciliation).
- Why it is fast: real DOM operations trigger layout and paint and are expensive; JavaScript object comparison is cheap. React also batches multiple state updates in one render pass.
- Diffing heuristics (O(n) instead of O(n³)): (a) elements of a *different type* are destroyed and rebuilt, not patched; (b) children of a list are matched by a stable key.
- React is a library, not a framework: it covers the view layer only. Routing (React Router), state management (Redux, Zustand, Context) and data fetching (Axios, React Query) are separate choices. Angular, by contrast, is a full framework.
- React Fiber (React 16+) is the rewritten reconciler that can split rendering into units of work, pause, and prioritise urgent updates (typing) over slow ones.
JSX and elements
- JSX is a syntax extension that lets HTML-like markup sit in JavaScript. It is not understood by browsers — Babel transpiles it to
React.createElement(type, props, ...children), which returns a plain object called a React element. - JSX rules: return exactly one root element (use a Fragment
<>...</>to avoid an extra div); JavaScript expressions go inside single curly braces; attributes use camelCase —className(notclass, a reserved word),htmlFor,onClick,tabIndex; every tag must be closed, including<br />and<img />. - Inline styles take an object with camelCased properties:
style={ { color: 'red', fontSize: 14 } }— the outer brace is the JSX expression, the inner one the object literal. - Component names must start with a capital letter. Lowercase names are treated as HTML tags, so
<button>is the DOM button and<Button>is your component. - JSX escapes embedded values before rendering, which prevents most XSS injection;
dangerouslySetInnerHTMLdeliberately bypasses this and is named to discourage its use. - Comments inside JSX are written as a JavaScript expression containing a block comment.
Components, props and state
- Function component: a JavaScript function that takes a props object and returns JSX. This is the modern standard.
- Class component: extends
React.Componentwith arender()method andthis.state; still examined in universities and legacy code, but new code uses functions and hooks. - Props — inputs passed from parent to child, like function arguments. Read-only / immutable: a child must never assign to its props. Unidirectional (one-way, top-down) data flow.
- State — data owned and mutated inside a component; changing it re-renders that component and its subtree.
- **
props.children** — whatever a component wraps, allowing layout/wrapper components (<Card><p>hi</p></Card>). - Default and typed props: default values via destructuring defaults; type checking via the
prop-typespackage or, in industry, TypeScript. - Lifting state up: when two siblings need the same data, move the state to their nearest common parent and pass down the value plus a setter callback. Child-to-parent communication therefore happens through callback props.
- Composition over inheritance: React components are combined by nesting and
children, not by extending one another. - Pure component rule: rendering must be side-effect free — do not mutate props, do not call APIs during render, do not write to the DOM directly.
Hooks (React 16.8 onwards)
- Rules of hooks: call them only at the top level (never inside a condition, loop or nested function) and only from a React function component or another hook. React matches hook state to the component by call order, so a conditional call corrupts the mapping. ESLint plugin
eslint-plugin-react-hooksenforces this. - **
useState(initial)** returns[value, setValue]. The setter is asynchronous and batched — read the updated value in the next render, not on the next line. For updates based on the previous value, use the functional formsetCount(c => c + 1). - State is immutable: never do
arr.push(x)orobj.k = v; create new references —setArr([...arr, x]),setObj({...obj, k: v})— otherwiseObject.iscomparison sees no change and the UI does not update. - **
useEffect(fn, deps)** runs side effects *after* paint — data fetching, subscriptions, timers, manual DOM work, logging.- No dependency array → runs after every render.
[]→ runs once after mount (equivalent tocomponentDidMount).[a, b]→ runs after mount and wheneveraorbchange (shallowObject.iscomparison).- A function returned from the effect is the cleanup, run before the next effect and on unmount (
componentWillUnmount) — clear timers, abort fetches, remove listeners.
- **
useRef(initial)** holds a mutable.currentthat survives renders without causing one — used for DOM handles (<input ref={inputRef}/>theninputRef.current.focus()), timer ids, and previous values. - **
useContext(MyContext)** reads a value from the nearest<MyContext.Provider value={...}>ancestor — solves prop drilling (threading a prop through many intermediate components) for themes, logged-in user, language. - **
useReducer(reducer, initialState)** — for complex or interrelated state;dispatch({type, payload})into a pure reducer. The Redux pattern without Redux. - **
useMemo(fn, deps)caches a computed value;useCallback(fn, deps)caches a function identity** so memoised children do not re-render.useCallback(f, d)isuseMemo(() => f, d). - **
React.memo(Component)** is a higher-order component that skips re-render when props are shallowly equal. - Custom hooks: any function named
useSomethingthat calls other hooks — the official way to share stateful logic (e.g.useFetch,useLocalStorage), replacing the old HOC and render-prop patterns. - Other hooks:
useLayoutEffect(runs synchronously before paint, for measurements),useId(stable unique ids for accessibility),useTransition/useDeferredValue(mark updates non-urgent, React 18).
Rendering: lists, keys, conditionals, forms and events
- Lists: render with
array.map(item => <li key={item.id}>{item.name}</li>). - Keys must be stable, unique among siblings and predictable. They let the diff algorithm match elements between renders. Array index as key is a bug when the list can be reordered, inserted into or filtered — state attaches to the wrong row. Use a database id. Keys are used by React only; they are not readable as props.
- Conditional rendering: ternary
{ok ? <A/> : <B/>}, logical AND{list.length > 0 && <List/>}, earlyreturn nullto render nothing. Beware{count && <X/>}— whencountis 0 React prints0on screen; writecount > 0 &&. - Controlled component: the form element's
valuecomes from state andonChangeupdates that state — React is the single source of truth; needed for validation, formatting and disabling submit. - Uncontrolled component: the DOM keeps the value and it is read through a ref — simpler, used for file inputs (
<input type="file">is always uncontrolled). - Events: camelCase handlers taking a function, not a string —
onClick={handleClick}(writingonClick={handleClick()}calls it during render). React attaches one listener at the root and delivers a SyntheticEvent, a cross-browser wrapper. Usee.preventDefault(); returningfalsedoes not stop default behaviour as it does in plain HTML. - Lifecycle, class vs hooks: mounting (
constructor→render→componentDidMount), updating (render→componentDidUpdate), unmounting (componentWillUnmount). With hooks, all three map ontouseEffectwith the right dependency array and cleanup. - StrictMode in development deliberately double-invokes render and effects to surface impure code; it does not happen in production builds.
Ecosystem, tooling and performance
- Project setup:
npm create vite@latest myapp -- --template reactis the current default; Create React App (npx create-react-app) is the older tool, now deprecated by the React team. Next.js is the recommended full-stack/SSR framework. - Build chain: Node.js and npm/yarn/pnpm → Babel (JSX and modern JS) → bundler (Vite/esbuild, Webpack) → static
dist/buildfolder served by any web server or CDN. - Routing:
react-router-dom—<BrowserRouter>,<Routes>,<Route path element>,<Link>(never a plain<a>for internal links, which reloads the page),useNavigate,useParams. This gives a Single Page Application (SPA): one HTML file, JavaScript swaps views. - State management beyond Context: Redux Toolkit (store, slices, actions, reducers, selectors), Zustand, Jotai; server state with React Query/TanStack Query or SWR.
- Data fetching:
fetchor Axios insideuseEffect, with loading, error and empty states handled; always clean up withAbortControllerto avoid setting state after unmount. - Performance tools:
React.lazy+<Suspense>for code splitting,React.memo,useMemo/useCallback, virtualised lists (react-window) for thousands of rows, and the React DevTools Profiler. - Testing: Jest + React Testing Library (query by what a user sees — role, label, text), Vitest with Vite.
- Rendering strategies: CSR (plain React — blank HTML until JS loads, weaker SEO), SSR/SSG (Next.js — HTML from the server, better SEO and first paint), hydration attaching event handlers to server HTML.
- Server Components (React 18/19, via Next.js App Router) run on the server and ship no JavaScript to the browser; client components are marked
'use client'. Treat specifics as fast-moving. - Accessibility and i18n: semantic tags,
aria-*attributes, label-input association; Indian products commonly need multi-language UI (react-i18next) and rupee/date formatting viaIntl.NumberFormat('en-IN', {style:'currency', currency:'INR'}).
Key points
- React - a JavaScript library for building component-based user interfaces; view layer only.
- Declarative UI - UI = f(state); describe the result, React performs the DOM steps.
- Virtual DOM - in-memory tree diffed against the previous tree so only changed nodes touch the real DOM.
- Reconciliation - the diff + patch process; different element types are replaced wholesale, lists matched by key.
- JSX - HTML-like syntax compiled by Babel into
React.createElementcalls; one root, camelCase attributes,className. - Component - a function returning JSX; name must start with a capital letter.
- Props - read-only inputs from parent to child; one-way data flow.
- State - internal mutable data; setting it schedules a re-render.
- Lifting state up - shared data moves to the nearest common ancestor and comes back down as props.
- Hooks - functions adding state and lifecycle to function components; only at the top level, only in components or hooks.
- useState -
[value, setValue]; setter is batched and asynchronous; usesetX(prev => ...)for dependent updates. - useEffect - side effects after render;
[]= once on mount; returned function is cleanup on unmount. - Immutability - always create a new array/object; React compares references with
Object.is. - Keys - stable unique ids for list items; array index breaks reordering and filtering.
- Controlled component - form value held in state with
onChange; uncontrolled values live in the DOM, read via refs. - Context - share values down the tree without prop drilling; not a replacement for Redux at large scale.
- React.memo / useMemo / useCallback - skip re-renders and recomputation; measure first, they are not free.
- SPA with React Router - client-side routing with
<Link>; Next.js adds SSR/SSG for SEO.
Formulas and examples
DOM operations per update ≈ number of changed nodes, not total nodes — reconciliation cost O(n) in tree size n, with keyed list matching.
Re-render trigger: component re-renders when (its own state changes) OR (its props change) OR (its parent re-renders) OR (a consumed context value changes).
useCallback(fn, deps) ≡ useMemo(() => fn, deps); React.memo compares props shallowly with Object.is.Bundle budget rule of thumb: initial JS under ~200 KB gzipped for acceptable load on a 4G Indian mobile connection; split the rest with
React.lazy.Example 1 — Counter with useState (the standard lab/interview warm-up)
import { useState } from 'react';
function Counter({ step = 1 }) {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(c => c + step)}>+{step}</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Trace with step = 1, three clicks on "+1": initial render count = 0 → click 1 schedules 0+1 → re-render shows 1 → click 2 shows 2 → click 3 shows 3. If the handler were written setCount(count + 1); setCount(count + 1); in one click, both calls read the same stale count = 0, so the result is 1, not 2; with the functional form setCount(c => c + 1) twice the result is 2.
Example 2 — Virtual DOM saving: counting real DOM writes
A table of 1,000 rows; one cell's text changes.
| Approach | Real DOM nodes touched | Approx. work |
|---|---|---|
Re-render table with innerHTML | 1,000 rows rebuilt (≈ 5,000 nodes at 5 cells/row) | Full re-parse, layout and paint |
| React with stable keys | 1 text node patched | ≈ 5,000 VDOM object comparisons (cheap JS) + 1 DOM write |
If one real DOM mutation plus layout costs about 1 ms and one VDOM node comparison about 0.001 ms, the imperative rebuild ≈ 5,000 × 1 ms = 5,000 ms, while React ≈ (5,000 × 0.001) + 1 = 6 ms — roughly 830× faster. (Time figures are illustrative orders of magnitude, not measured constants; the ratio of DOM writes is exact: 1 versus 5,000.)
Example 3 — Fetching with useEffect, including cleanup and the three UI states
function JobList({ city }) {
const [jobs, setJobs] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const ctrl = new AbortController();
setLoading(true);
fetch(`/api/jobs?city=${city}`, { signal: ctrl.signal })
.then(r => { if (!r.ok) throw new Error(r.status); return r.json(); })
.then(data => { setJobs(data); setError(null); })
.catch(e => { if (e.name !== 'AbortError') setError(e.message); })
.finally(() => setLoading(false));
return () => ctrl.abort(); // cleanup on unmount or city change
}, [city]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Could not load: {error}</p>;
if (jobs.length === 0) return <p>No jobs in {city}</p>;
return <ul>{jobs.map(j => <li key={j.id}>{j.title} - Rs {j.ctc}</li>)}</ul>;
}
Effect-run count when the user changes city twice: mount with city='Kochi' → 1 run; change to 'Pune' → cleanup + run 2; change to 'Delhi' → cleanup + run 3. Total 3 effect runs, 3 cleanups (the last on unmount). With [] instead of [city] the list would stay stuck on Kochi — a classic exam trap.
Example 4 — Index as key: the off-by-one bug, counted
List ['A','B','C'] rendered as <Input key={index} defaultValue={item}/>; the user types into row 1 (B). Now 'A' is deleted.
| Row after delete | Item | Key with index | Key with id | What React does |
|---|---|---|---|---|
| 0 | B | 0 | id-B | index: reuses old row 0 (A's node, still showing "A") — wrong text |
| 1 | C | 1 | id-C | index: reuses old row 1 (B's node, holding the typed text) — wrong text |
| — | A removed | 2 unmounted | id-A unmounted | index: the *last* node is destroyed; id: React removes exactly A's node |
With index keys, 2 of 2 remaining rows show the DOM state left by whichever item previously sat at that index; with stable ids, 0 of 2 are wrong and only 1 node is removed.
Example 5 — Render-count arithmetic with React.memo
Parent holds count and renders <Child data={list} onPick={handlePick}/>. The parent's state updates 10 times.
| Setup | Child renders out of 10 parent renders |
|---|---|
Plain Child | 10 — a parent re-render re-renders children by default |
React.memo(Child), onPick defined inline | 10 — a new function identity each render breaks shallow equality |
React.memo(Child) + useCallback(handlePick, []) + useMemo for list | 1 — only the first render |
Saving = 9 of 10 child renders avoided (90%). If each child render costs 4 ms, time saved = 9 × 4 = 36 ms.
Where it is used
- IT services and GCCs: TCS, Infosys, Wipro, Cognizant, Accenture, Capgemini, HCLTech and LTIMindtree staff React front ends for banking, insurance and retail clients; "React + Spring Boot" and "MERN stack" are the two commonest fresher JD combinations in India.
- Indian product companies and startups: Zomato, Swiggy, Flipkart, Meesho, PhonePe, Razorpay, Groww, CRED, Zoho and Freshworks build customer and internal dashboards in React or React Native.
- Government and public digital services: several Digital India / state portals, dashboards and admin consoles use React SPAs over REST APIs; React Native powers many banking and fintech mobile apps for Android.
- Job roles: Front-end Developer, React Developer, MERN/Full Stack Developer, UI Engineer, React Native Developer; fresher CTC commonly Rs 3.5-6 lakh in services firms and Rs 8-25 lakh in product firms, varying by year, city and role.
- Internal enterprise tools: admin panels, analytics dashboards, HRMS/ERP front ends, hospital and school management consoles, where component libraries (Material UI, Ant Design, shadcn/ui, Tailwind) speed delivery.
- Academics: final-year mini and major projects (job portals, e-commerce, attendance systems), Web Technology / Full Stack Development labs, and NPTEL / AICTE-aligned elective coursework.
Interview questions
- Q: What is the virtual DOM and how does it make React fast? A: It is an in-memory JavaScript representation of the UI; React diffs the new tree against the old one and applies only the changed nodes to the real DOM, avoiding expensive layout and repaint of unchanged parts.
- Q: Difference between props and state? A: Props are read-only inputs passed in by the parent; state is private data owned by the component. Both trigger a re-render when they change, but only the owner can change state.
- Q: Why must a React component be a pure function of its props and state? A: Purity lets React re-render safely at any time, reuse results, and in StrictMode double-invoke renders without side effects; API calls and DOM writes belong in effects or event handlers.
- Q: Why do keys matter in a list, and why is the array index a bad key? A: Keys tell the reconciler which element is which between renders; if the list is reordered or an item is removed, index keys shift and React reuses the wrong DOM node and its internal state.
- Q:
useStatesetter is called twice withsetCount(count + 1)in one handler — what is the result? A: The count increases by one only, because both calls read the same stale value from that render; the functional formsetCount(c => c + 1)increases it by two. - Q: What are the rules of hooks and why do they exist? A: Hooks must be called at the top level of a component or another hook, never conditionally, because React identifies each hook's state by its call order on every render.
- Q: How do you replicate
componentDidMount,componentDidUpdateandcomponentWillUnmountwith hooks? A:useEffect(fn, [])for mount,useEffect(fn, [deps])for update, and the function returned from the effect for unmount cleanup. - Q: Controlled vs uncontrolled component? A: A controlled input's value comes from state with an
onChangehandler, so React is the single source of truth; an uncontrolled input keeps its value in the DOM and is read through a ref. - Q: What is prop drilling and how do you avoid it? A: Passing a prop through many intermediate components that do not use it; avoid it with Context, component composition (
children), or a state library such as Redux Toolkit or Zustand. - Q: When would you use
useMemo,useCallbackandReact.memo? A: When profiling shows a genuinely expensive computation or an unnecessary re-render of a heavy memoised child; applied blindly they add memory and comparison cost without benefit. - Q: Is Context a replacement for Redux? A: No — Context is a transport mechanism for a value, with no selectors, middleware or devtools, and every consumer re-renders when the value changes; Redux Toolkit suits large, frequently-updated shared state.
- Q: Why does React Router use
<Link>instead of<a>? A:<a>triggers a full page reload, losing SPA state;<Link>intercepts the click and updates the History API, swapping components on the client. - Q: What problem does Next.js solve over plain React? A: Plain React is client-rendered, so the initial HTML is nearly empty, hurting first paint and SEO; Next.js provides server rendering, static generation, routing and API routes.
Common mistakes
- Mutating state directly —
arr.push(x)orstate.obj.k = vthen calling the setter with the same reference; React sees no change and nothing re-renders. - Expecting the state setter to be synchronous — logging the variable on the next line prints the old value; the new value appears in the next render.
- **Missing or wrong
useEffectdependencies** — omitting a dependency freezes stale data; including an object or function recreated every render causes an infinite fetch loop; calling a setter inside an effect with no array loops forever. - Using the array index as a key in a list that can reorder, filter or delete — rows keep the previous row's input text, checkbox or focus.
- Calling the handler instead of passing it —
onClick={doIt()}runs during render; it must beonClick={doIt}oronClick={() => doIt(id)}. - Breaking the rules of hooks — a
useStateinside anifor a loop, or an earlyreturnbefore a hook, which shifts the hook order and throws. - Forgetting cleanup — timers, socket subscriptions and in-flight fetches left running after unmount cause memory leaks and "state update on unmounted component" warnings.
- **Writing
class,for, or lowercase component names in JSX**, and the{count && <X/>}trap that prints a literal0when the count is zero.
Related topics
Web fundamentals: HTML, CSS and JavaScript · REST APIs · Back-end development with Node.js · Authentication and authorisation: sessions, OAuth and JWT · System design basics for freshers · Caching, queues and CDNs