Back to Notes

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

CDNNPM + Bundler
Use forPrototypes, learningProduction apps
Setup<script> tagsnpm init + bundler
Tree shakingNoYes

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

PrefixMeaningExample range
~1.2.3Patch updates only>=1.2.3 <1.3.0
^1.2.3Minor + 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 className not class (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

TechniqueWhen to use
React.memoPrevent child re-render when props unchanged
useMemoSkip expensive recalculation
useCallbackStable function ref for memoized children
Code splitting (React.lazy)Large bundles, route-level splitting
VirtualizationLong 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
  • key prop 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

QuestionAnswer
React vs Angular vs VueReact = UI library (flexible), Angular = full framework (opinionated), Vue = progressive framework
Virtual DOM benefitBatch DOM updates → fewer expensive real DOM repaints
When does re-render happen?State or props change, parent re-renders, context changes
Class vs Function componentsFunction components + hooks are the modern standard; class components are legacy
Controlled vs UncontrolledControlled = 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