React Fundamentals
React builds UIs from declarative components. Understand JSX, props, state, hooks and the render/reconcile cycle so you can build maintainable interactive interfaces.
What is this?
React is a library for building user interfaces from composable components. Components are functions that return JSX — a JavaScript syntax extension describing the UI as a tree of elements. React renders that tree, and when state or props change it re-runs components, diffs the new tree against the previous one, and applies the minimal DOM updates.
Why do you need it?
- The UI is declared from data, so the code shows what should render instead of how to mutate the DOM.
- Components compose, so buttons, cards and pages are reusable and individually testable.
- Unidirectional data flow — props down, events up — makes large UIs predictable.
- A huge ecosystem of hooks and components means most needs already have a maintained solution.
- Frameworks like Next.js and React Native reuse the same component model across web, server and mobile.
Mental model
A restaurant kitchen
React rendering
The menu describes each dish (components). When an order arrives, the kitchen cooks it from ingredients (state and props). If the order changes, the kitchen only replaces the dishes that actually changed — like React's reconciliation updating only the DOM nodes that differ.
How it works
- A component function runs and returns JSX, which the JSX transform compiles into element objects.
- useState returns the current value plus a setter; calling the setter schedules a re-render of that component.
- On re-render, React builds the new element tree and reconciles it against the previous one.
- The diff identifies changed props, state and keys, then updates only those DOM nodes in the commit phase.
- Effects (useEffect) run after the browser paints, which is where subscriptions, timers and network reads belong.
The React render and commit cycle.
Key commands
npm create vite@latest my-app -- --template reactScaffold a new React project with Vite.
Scaffolding project in my-app...
npm installInstall project dependencies.
npm run devStart the dev server with hot reload.
Local: http://localhost:5173/
npm run buildCreate an optimized production build.
npm run lintRun the ESLint config, including react-hooks rules.
npm run previewServe the production build locally to verify it.
Examples
A counter with state and an effect
import { useState, useEffect } from "react"; export default function Counter() { const [count, setCount] = useState(0); useEffect(() => { document.title = "Count: " + count; }, [count]); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount((c) => c + 1)}>Increment</button> </div> );}useState(0) creates the count state. Each click calls the setter with a function, which updates count and schedules a re-render. useEffect synchronizes the document title whenever count changes, and its dependency array keeps the effect scoped to exactly that state.
In production
- Interactive applications such as dashboards, editors and commerce flows where DOM manipulation would be unmanageable.
- React serves as the rendering layer inside Next.js, Remix and other meta-frameworks for SSR and static output.
- Design-system teams ship component libraries that other products consume as a package.
- React Native reuses component thinking for iOS and Android apps.
Best practices
- Keep components pure: same props and state always produce the same output, with side effects moved into effects or handlers.
- Use keys that are stable and unique; prefer data ids over array indexes.
- Lift state to the lowest common ancestor so sibling components can share it.
- Extract repeated logic into custom hooks with clear, typed signatures.
- Set up eslint-plugin-react-hooks so dependency and hook-order mistakes fail CI.
Common mistakes
- Mutating state in place (state.push) so React misses the change and nothing re-renders.
- Using array indexes as keys, which corrupts list state during reordering.
- Omitting dependencies from useEffect and reading stale closures.
- Putting every shared value in Context and re-rendering the whole tree on every change.
Common issues
Too many re-renders. React limits the number of rendersA state update is triggered during render instead of in an event handler or effect.
Wrap the update in a click handler or effect rather than calling the setter directly in the render body.
Each child in a list should have a unique keyList items lack a key or reuse the array index.
Assign a stable key such as item.id to the top-level element of each item.
Effects run on every renderThe dependency array is missing or includes unstable values.
Provide a dependency array and memoize object/function props with useMemo and useCallback.
Debugging tips
- Install the React DevTools browser extension to inspect the component tree, props and hook values.
- Run StrictMode in development to surface double-rendering bugs and unsafe effects.
- Trace re-renders with the Profiler tab to find components updating on every parent render.
- Log state transitions in the setter or event handler, not inside the render body, to avoid infinite loops.
Interview checkpoint
Hands-on lab
- Scaffold a React project with Vite
- Manage a list with useState
- Lift state and pass callbacks as props
- Render lists with stable keys
Build a Task Tracker with Hooks
35 min · intermediate
Quick quiz
Which hook returns a value and a function to update it?
Mutating state with push() reliably triggers a re-render.
What is the recommended way to give list items a stable identity?
Cheat sheet
useState(0)Local state with a setteruseEffect(fn, deps)Run effects after paintuseContext(Ctx)Read from a provideruseReducer(reducer, init)State with update logicuseMemo / useCallbackMemoize values and callbacks{expression}Embed a JavaScript valueonClick={handleClick}Attach an event handlerclassNameHTML class, renamed in JSXitems.map((i) => <li key={i.id}>)Render a list