React
Core Concepts
- Library (not framework) — only controls the root node you give it
- Virtual DOM — React diffs a virtual copy of the DOM and applies minimal real DOM updates
- JSX — HTML-like syntax transpiled to
React.createElement()calls by Babel (browsers don't understand JSX natively) - One-way data flow — parent → child via props; child communicates up via callbacks
Setup & Tooling
How React Works
React takes control of the single root node you give it (<div id="root">) and replaces its content on every render. Everything outside that node is untouched — which is why React is a library, not a framework.
<!-- index.html -->
<div id="root"></div>
<script src="index.js"></script>
// index.js
import ReactDOM from 'react-dom/client';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
CDN vs NPM
| CDN | NPM + Bundler | |
|---|---|---|
| Use for | Prototypes, learning | Production apps |
| Setup | <script> tags | npm init + bundler |
| Tree shaking | No | Yes |
crossorigin attribute on <script> tags: use anonymous for public scripts (no credentials sent), use-credentials when the server needs to identify the requester.
NPM
NPM is the default package manager for Node.js (not an acronym).
npm init # creates package.json
npm install <pkg> # adds to dependencies
npm install -D <pkg> # adds to devDependencies
Dev dependency — needed during development only (bundler, linter, types). Dependency — needed in both dev and production (React, axios).
Semver in package.json
| Prefix | Meaning | Example range |
|---|---|---|
~1.2.3 | Patch updates only | >=1.2.3 <1.3.0 |
^1.2.3 | Minor + patch updates | >=1.2.3 <2.0.0 |
Parcel (Bundler)
npx parcel index.html # dev server with HMR
npx parcel build index.html # production build
Parcel provides out of the box: HMR (Hot Module Replacement), faster builds via caching, image optimisation, minification, bundling, compression, code splitting, differential bundling (older browser support), tree shaking.
Alternatives: Vite (faster, ES module-based), Webpack (most configurable).
JSX
JSX is HTML-like syntax for JavaScript — it is not valid JS and browsers can't run it natively. Babel transpiles JSX to React.createElement() calls at build time.
// JSX
const el = <h1 className="title">Hello</h1>;
// What Babel compiles it to
const el = React.createElement('h1', { className: 'title' }, 'Hello');
JSX rules:
- Use
classNamenotclass(reserved word in JS) - All tags must be closed (
<img />) - Return one root element (wrap in
<>...</>if needed) - JS expressions inside
{}
Hooks
useState
const [count, setCount] = useState(0);
// State updates are async — don't read state right after setting
// Use functional form when new state depends on old:
setCount(prev => prev + 1);
useEffect
useEffect(() => {
// runs after render
return () => { /* cleanup */ };
}, [dependency]); // [] = mount only, [dep] = when dep changes, no array = every render
Common pitfall: missing dependency → stale closure. Add all values used inside to the array.
useRef
const inputRef = useRef(null);
// Access DOM node
<input ref={inputRef} />
inputRef.current.focus();
// Store mutable value that doesn't trigger re-render
const timerRef = useRef(null);
timerRef.current = setTimeout(...);
useMemo / useCallback
// Memoize expensive computed value
const sorted = useMemo(() => items.sort(), [items]);
// Memoize function reference (for passing to child components)
const handleClick = useCallback(() => doSomething(id), [id]);
Use sparingly — premature memoization adds complexity for minimal gain.
useContext
const ThemeContext = React.createContext('light');
// Provider
<ThemeContext.Provider value="dark">
<Child />
</ThemeContext.Provider>
// Consumer
const theme = useContext(ThemeContext);
useReducer
For complex state logic with multiple sub-values:
function reducer(state, action) {
switch (action.type) {
case 'increment': return { count: state.count + 1 };
case 'reset': return { count: 0 };
default: throw new Error();
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'increment' });
Component Patterns
Controlled vs Uncontrolled
// Controlled — React owns the value
const [val, setVal] = useState('');
<input value={val} onChange={e => setVal(e.target.value)} />
// Uncontrolled — DOM owns the value
const inputRef = useRef();
<input ref={inputRef} defaultValue="initial" />
inputRef.current.value; // read when needed
Lifting State Up
When two components need to share state, move it to their closest common ancestor and pass down via props.
Composition over Inheritance
// children prop
function Card({ children }) {
return <div className="card">{children}</div>;
}
// render props
function Toggle({ render }) {
const [on, setOn] = useState(false);
return render(on, () => setOn(o => !o));
}
Custom Hooks
Extract stateful logic into reusable functions:
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(url).then(r => r.json()).then(d => {
setData(d);
setLoading(false);
});
}, [url]);
return { data, loading };
}
Performance
| Technique | When to use |
|---|---|
React.memo | Prevent child re-render when props unchanged |
useMemo | Skip expensive recalculation |
useCallback | Stable function ref for memoized children |
Code splitting (React.lazy) | Large bundles, route-level splitting |
| Virtualization | Long lists (react-window, react-virtual) |
// Lazy loading
const Heavy = React.lazy(() => import('./Heavy'));
<Suspense fallback={<Spinner />}>
<Heavy />
</Suspense>
Reconciliation (Diffing)
- React compares the new virtual DOM tree against the previous one
- Elements with different type → unmount old, mount new
- Elements with same type → update props in place
keyprop helps React identify which list items changed, were added, or removed- Use stable, unique IDs as keys — never array index for dynamic lists
Common Interview Questions
| Question | Answer |
|---|---|
| React vs Angular vs Vue | React = UI library (flexible), Angular = full framework (opinionated), Vue = progressive framework |
| Virtual DOM benefit | Batch DOM updates → fewer expensive real DOM repaints |
| When does re-render happen? | State or props change, parent re-renders, context changes |
| Class vs Function components | Function components + hooks are the modern standard; class components are legacy |
| Controlled vs Uncontrolled | Controlled = React state owns value; Uncontrolled = DOM owns value via ref |
| What is reconciliation? | React's algorithm to diff virtual DOM trees and update only changed real DOM nodes |