React_

A beginner-friendly React guide covering JSX, components, hooks, architecture, rendering, and lifecycle.

Getting Started

React is a JavaScript library for building user interfaces out of composable, reusable components. It uses a declarative model: you describe what the UI should look like for a given state, and React figures out how to update the DOM to match. You never manually write document.createElement or .appendChild — you describe the end result, and React handles the DOM manipulation for you.

Installation

Project Structure

A Minimal Component


Architecture & Rendering

Before diving into syntax, it's worth understanding what React is actually doing behind the scenes. This section explains the mental model that everything else in this guide builds on.

JSX Compiles to Function Calls

JSX isn't HTML — it's syntax sugar. Every JSX element compiles down to a React.createElement() call, which returns a plain JavaScript object describing what you want rendered. React never touches the real DOM at this step; it's just building a description of it.

That plain object tree is called the Virtual DOM: a lightweight, in-memory representation of what the UI should look like. It's just JavaScript objects — cheap to create and compare, unlike real DOM nodes, which are expensive to create and mutate.

Reconciliation: How React Decides What to Update

Every time a component's state or props change, React doesn't throw away the real DOM and rebuild it from scratch. Instead it re-runs the component function to get a new Virtual DOM tree, then compares it against the previous Virtual DOM tree. This comparison process is called reconciliation.

React walks both trees and only applies the minimal set of real DOM changes needed to make the browser match the new tree — this final step is called the commit phase. If only a button's text changed, React updates that one text node, not the entire page.

This is why key matters so much when rendering lists (covered later): keys are how React matches up old and new tree nodes during reconciliation. Without a stable key, React can't reliably tell "this is the same item, just moved" from "this is a brand new item," which leads to bugs like input fields losing their value when a list reorders.

What Actually Triggers a Re-render

A component re-renders when its state changes (via a setter like setCount), when its props change, or when its parent re-renders. "Re-render" means React calls the function component again, top to bottom — the entire function body re-executes, not just the part that changed.

This is a common surprise for beginners: nothing in a function component "runs once and stays." Every render re-executes every line, which is why expensive calculations sometimes need useMemo (covered in the hooks section) to avoid redoing work unnecessarily.

Unidirectional Data Flow

React data flows in one direction: props flow down, events flow up. A parent passes data to a child via props; the child never directly modifies its parent's data. If a child needs to change something in the parent, the parent passes down a function as a prop, and the child calls that function — it hands the event back up rather than reaching up to mutate state directly.

This one-way flow makes apps easier to debug: if a value is wrong, you only need to trace it in one direction (up through the props it was passed) instead of hunting through code that could have mutated it from anywhere.


JSX Basics

JSX is a syntax extension that lets you write HTML-like markup inside JavaScript, using the rules explained above — it compiles to React.createElement calls that build a Virtual DOM tree.

Expressions & Attributes

Any valid JavaScript expression goes inside curly braces. Attributes use camelCase instead of lowercase HTML.

className & Fragments

class is reserved in JavaScript, so JSX uses className. Fragments group elements without adding an extra DOM node — useful because a component can only return a single root element.

Conditional Rendering with Ternaries

JSX has no if statements inside markup, so conditionals rely on expressions. The ternary operator is the most common pattern for choosing between two outputs inline.

More conditional rendering patterns (the && operator and early returns) are covered in their own section further down, once components and state have been introduced.


Components

Components are reusable, self-contained pieces of UI — JavaScript functions that return JSX. Modern React almost exclusively uses functional components (as opposed to older class-based components).

Functional Components & Props

Props pass data from a parent component down to a child, following the unidirectional data flow described above.

Default Props

Destructured parameters can define fallback values directly, used whenever a prop isn't passed in.

Children

The special children prop holds whatever is nested between a component's tags. This enables the composition pattern (wrapping components around content) mentioned in the Tips section.


State

State lets a component remember and update data between renders. Without state, a component would forget everything and reset to its initial values on every re-render.

useState & Updating State

useState returns the current value and a setter function. Calling the setter schedules a re-render — it does not mutate the original variable, and it doesn't update the value immediately in the same line of code.

State Updates Are Asynchronous and Batched

This trips up almost every beginner at some point: calling setCount does not update count right away. React schedules the update and re-renders the component later, batching multiple state updates together into a single re-render for performance.

The fix is the functional update form, covered next — it's the same category of problem javascript.mdx's let-in-a-loop example demonstrates: using a stale value captured at the wrong time.

Functional Updates

When new state depends on the previous value, pass a function to the setter instead of a plain value. React guarantees this function receives the most up-to-date state, even across multiple queued calls.


Effects & the Component Lifecycle

Older React (class components) had explicit lifecycle methods: componentDidMount, componentDidUpdate, and componentWillUnmount. Function components don't have these methods — instead, useEffect covers all three phases, controlled by its dependency array. If you've seen lifecycle methods in a tutorial before, this table is the mental bridge:

| Lifecycle phase | Class component method | Hook equivalent | |---|---|---| | Mount (first render) | componentDidMount | useEffect(fn, []) | | Update (on specific changes) | componentDidUpdate | useEffect(fn, [dep1, dep2]) | | Unmount (removed from screen) | componentWillUnmount | cleanup function returned from useEffect |

Effects let components synchronize with external systems — APIs, subscriptions, timers, or the DOM — after the render has already happened and the DOM has been updated.

useEffect & Dependency Array

The dependency array controls when the effect re-runs. An empty array means "mount only" — it runs once, after the very first render, and never again.

Leaving the array off entirely makes the effect run after every render (rarely what you want); an empty array [] makes it run only once, on mount.

Cleanup Functions (the Unmount Phase)

Returning a function from an effect cleans up subscriptions or timers. React calls this cleanup function right before the component is removed from the screen (unmount), and also right before the effect re-runs due to a dependency change — preventing duplicate timers or leaked subscriptions.


Event Handling

React wraps native DOM events in a consistent, cross-browser system called SyntheticEvent.

onClick & onChange

Form Submission

Call preventDefault() to stop the browser's default full-page reload.


More Conditional Rendering Patterns

Now that components and state are covered, here are the other two conditional rendering patterns you'll use constantly, beyond the ternary shown earlier.

&& Operator

Renders something, or nothing at all — useful when there's no "else" case.

Early Return

Guards an entire component against an invalid or loading state, returning different JSX before the "main" render logic runs.


Lists & Keys

map() & Keys

Use Array.prototype.map to turn an array of data into an array of elements. Keys help React's reconciliation process (explained in Architecture & Rendering) identify which items changed, were added, or were removed — and must be unique among siblings.

Best Practices


Forms

Controlled Components

A controlled component keeps its value in React state, making state the single source of truth instead of the DOM. This fits the unidirectional data flow model: the input's displayed value always comes from state (down), and every keystroke updates that state (up) via onChange.


Common Hooks

A closer look at the hooks you'll reach for most often beyond useState and useEffect, each with a complete, runnable example.

useMemo — Memoizing an Expensive Value

useMemo re-runs a calculation only when its dependencies change, instead of on every render. Reach for it when a computation is genuinely expensive (sorting or filtering a large array, heavy math) — for cheap operations, it adds overhead without meaningful benefit.

useCallback — Memoizing a Function Reference

Every render creates brand-new function instances by default. useCallback keeps the same function reference across renders unless its dependencies change — important when passing callbacks to memoized child components, since a new function reference would defeat that memoization.

useRef — Persisting a Value Without Re-rendering

useRef holds a mutable value that survives across renders, but changing it does NOT trigger a re-render (unlike useState). It's most commonly used to directly access a DOM element.

useContext — Reading Shared Data Without Prop Drilling

Passing props down through five layers of components just so the bottom one can read a value is called "prop drilling." useContext lets any descendant read a value directly, as long as it's wrapped in that context's Provider — no need to pass it through every layer in between.


Tips & Best Practices


Common Mistakes

Missing Keys

Mutating State

Infinite useEffect Loops

Calling Hooks Conditionally

Forgetting Cleanup