| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
| Functional Components | The modern standard. Pure functions that take props and return JSX. React re-renders when state or props change. No this keyword. |
React 18+, React DevTools | Destructuring props, default props, children composition, named exports | react.dev – Your First Component |
| Class Components (Legacy) | Older lifecycle-based model (componentDidMount, componentDidUpdate). Still exists in many codebases — know how to read, not write |
React (any version) | Reading this.state, this.setState, lifecycle methods, migrating to hooks |
react.dev – Class Components |
| JSX | Syntactic sugar over React.createElement. Not HTML — class→className, for→htmlFor, self-closing tags required, only one root element |
Babel / esbuild (transpiles JSX) | Conditional rendering (&&, ternary), list rendering with .map() + key, fragments (<>) |
react.dev – JSX |
| Component Composition | Building UIs by combining small, focused components. Prefer composition over inheritance in React | — | Props + children pattern, slot pattern via named props, compound components, render delegation | react.dev – Passing JSX as children |
| Props & Data Flow | Unidirectional data flow — data flows down via props, events bubble up via callbacks. Enforces predictability | PropTypes (runtime), TypeScript (compile-time) | Props destructuring, callback props for child→parent communication, prop drilling (and when it becomes a problem) | react.dev – Passing Props |
| Event Handling | React uses synthetic events (cross-browser normalized wrapper over native events). All events are camelCase | — | onClick, onChange, onSubmit, e.preventDefault(), e.stopPropagation(), event delegation |
react.dev – Responding to Events |
| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
useState |
Declares state in a functional component. Re-renders the component on each state change. State updates are asynchronous (batched in React 18+) | — | Functional updater form setState(prev => ...), avoid mutating state directly, initializer function for expensive default |
react.dev – useState |
useEffect |
Synchronize component with an external system (fetch, DOM, subscriptions). Runs after render. Cleanup function prevents memory leaks | — | Dependency array, cleanup via return function, empty [] = run once on mount, avoid missing dependencies |
react.dev – useEffect |
useRef |
Mutable ref that doesn't trigger re-renders. Two uses: (1) access DOM nodes, (2) persist values across renders without re-rendering | — | DOM refs (ref.current for focus/scroll), storing previous values, forwardRef to expose refs to parent components |
react.dev – useRef |
useContext |
Consume values from a Context without prop drilling. Re-renders every consumer when context value changes | — | createContext, Provider wrapping, context splitting to avoid over-renders, combine with useMemo for stable values |
react.dev – useContext |
useMemo |
Cache an expensive computed value between renders. Only recomputes when dependencies change | — | Wrap CPU-intensive calculations, stabilize object/array references passed as props to memoized children | react.dev – useMemo |
useCallback |
Cache a function definition between renders. Needed when passing callbacks to React.memo children or as effect dependencies |
— | Wrap event handlers passed to memoized children, stabilize callbacks used in useEffect dependency arrays |
react.dev – useCallback |
useReducer |
Alternative to useState for complex state logic with multiple sub-values or when next state depends on previous state. Follows Redux-like pattern |
— | Action dispatch pattern, reducer purity, combining with useContext for lightweight global state |
react.dev – useReducer |
useLayoutEffect |
Like useEffect but fires synchronously after DOM mutations and before the browser paints. Use only when you need to read/write DOM layout |
— | Measuring DOM size/position, preventing visual flicker, tooltip/popover positioning | react.dev – useLayoutEffect |
useId (React 18) |
Generates stable unique IDs that are consistent across server and client. Solves hydration mismatch for accessibility IDs | — | Label–input association (htmlFor + id), ARIA attribute IDs |
react.dev – useId |
useTransition (React 18) |
Mark a state update as non-urgent so React can interrupt it for higher-priority updates (e.g., typing). Core to Concurrent React | — | Wrap slow state updates, isPending flag for loading UI, tab switching, search filtering on large lists |
react.dev – useTransition |
useDeferredValue (React 18) |
Defer re-rendering a part of the UI to keep the interface responsive. Similar to debounce but React-aware | — | Defer expensive child re-renders, show stale content while new content loads, combine with React.memo |
react.dev – useDeferredValue |
| Custom Hooks | Extract and reuse stateful logic between components. The most important abstraction pattern in React | — | use prefix convention, composing built-in hooks, returning values + setters, hook libraries (ahooks, react-use) |
react.dev – Custom Hooks |
| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
| Client-Side Routing | SPA navigation without full page reload. History API manipulation. Routes defined as a component tree | React Router v6+ (Reach Router is merged and deprecated — do not use) | <BrowserRouter>, <Routes>, <Route>, <Link>, <NavLink> (active state styling) |
React Router docs |
| Dynamic Routes & Params | URL segments as data. Nested routes with shared layout via <Outlet> |
React Router v6+ | useParams, useSearchParams, route loaders (data fetching at route level), <Outlet> for nested layouts |
React Router – Dynamic Segments |
| Programmatic Navigation | Navigate from code (after form submit, auth check, etc.) | React Router v6+ | useNavigate (replaces deprecated useHistory from v5), navigate(-1) for back, replace: true to avoid history stack entries |
React Router – useNavigate |
| Protected Routes | Guard routes behind authentication or authorization checks | React Router v6+, React context | Redirect unauthenticated users, role-based access, loader-based auth checks | React Router – Auth example |
| Route-level Code Splitting | Load component code only when the route is visited — reduces initial bundle | React.lazy, Suspense, React Router v6 |
Wrap lazy-imported components in <Suspense fallback={...}>, combine with route loaders |
react.dev – lazy |
| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
| Controlled Components | React state is the single source of truth for input values. Verbose but predictable and testable | useState |
value + onChange on every input, controlled select/checkbox/radio, forms as state snapshots |
react.dev – Forms |
| Uncontrolled Components | DOM is the source of truth. Use when integrating with non-React libraries or for simple forms where controlled is overkill | useRef |
ref on input, defaultValue, ref.current.value on submit — avoid for complex validation |
react.dev – Uncontrolled |
| Form Libraries | Manage form state, validation, error messages, and submission declaratively — reduces boilerplate significantly | React Hook Form (performant, minimal re-renders), Formik (older, more re-renders) | register, handleSubmit, formState.errors, field arrays, watch, setValue |
React Hook Form docs |
| Schema Validation | Declare validation rules as a schema separate from UI logic. Works with both RHF and Formik | Zod (TypeScript-first, preferred), Yup | zodResolver / yupResolver with RHF, type inference from schema (z.infer<typeof schema>), nested object validation |
Zod docs, RHF + Zod |
| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
| Native Fetch / Axios | Understand the base layer before reaching for libraries. Axios adds interceptors, automatic JSON parsing, request cancellation | fetch (built-in), Axios |
async/await, AbortController for cancellation, Axios request/response interceptors, error status handling |
MDN Fetch, Axios docs |
| Server State Management | Distinguish client state (UI) from server state (remote data). Server state needs caching, background refetching, staleness management — this is not what Redux is for | TanStack Query, SWR | Query keys strategy, staleTime vs cacheTime, mutations + invalidateQueries, optimistic updates, prefetching |
TanStack Query docs |
| Error Boundaries | React mechanism to catch render errors in a subtree and display a fallback UI. Essential for graceful degradation | react-error-boundary library |
<ErrorBoundary fallback={...}>, useErrorBoundary hook for imperative throws, per-route boundaries |
react-error-boundary |
| Suspense for Data | Declarative loading states. Component "suspends" while data loads; React shows the nearest <Suspense> fallback |
TanStack Query (experimental), Next.js (stable) | <Suspense fallback={<Skeleton />}>, streaming in Next.js App Router, use() hook in React 19 |
react.dev – Suspense |
| Skill | Core Concepts | Tools & Libraries | Key Techniques | Resources |
|---|---|---|---|---|
| Preventing Re-renders | A component re-renders when its state/props change OR when its parent re-renders. Understand this before optimizing | React DevTools Profiler | React.memo (memoize component), useMemo (memoize values), useCallback (memoize functions) — only add when profiler confirms a problem |
react.dev – Render Performance |
| Code Splitting | Load JavaScript only when needed. Route-level splitting is the highest-ROI optimization in most apps | React.lazy, Suspense, dynamic import() |
Lazy-load routes, lazy-load heavy modals/drawers, named chunk comments /* webpackChunkName */ |
react.dev – lazy |
| Virtualized Lists | Rendering thousands of DOM nodes destroys performance. Virtualization only renders visible items | react-window, TanStack Virtual | FixedSizeList, VariableSizeList, WindowScroller for page-level scroll, infinite scroll with virtualization |
react-window docs |
| Concurrent Features | React 18's scheduler can interrupt, pause, and resume renders. Use useTransition + useDeferredValue to mark non-urgent work |
React 18+ | startTransition for non-urgent state updates, useDeferredValue for derived expensive renders |
react.dev – Concurrent React |
| Profiling | Measure before optimizing. Guessing is wasteful; the Profiler shows exactly which components are slow and why | React DevTools Profiler, Chrome Performance tab | Record → identify slow commits → find the component → apply targeted fix | react.dev – Profiler |