HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
6 Topics
Chapterβ€’70 Minutes

React Foundations & Mental Model

Build a correct mental model of React, components, declarative UI, JSX, props, state, rendering, and reconciliation before moving into advanced APIs.

Why React Exists

React is a UI library built around a declarative programming model. Instead of manually finding DOM nodes and describing every mutation, you describe what the UI should look like for a given state. React then determines the updates needed to bring the rendered result in line with that description.

The important distinction is between imperative DOM programming and declarative UI programming. In an imperative application you might select a button, change its text, toggle a class, and update an ARIA attribute independently. In React, those values are derived from state and props. When state changes, the component function runs again and produces a new UI description.

React is especially useful when an application has many pieces of state that interact: forms, filters, asynchronous data, permissions, optimistic updates, animations, and nested reusable components. The library gives you a consistent way to model those relationships.

React does not replace JavaScript fundamentals. Arrays, closures, modules, promises, event handling, browser APIs, accessibility, HTTP, and CSS remain essential. A strong React developer uses React as one layer in a larger web platform.

Mental model

Think of a component as a function from inputs and state to a UI description. Rendering is a calculation; DOM mutation is an implementation detail managed by React.

Declarative UI Flow
A tiny declarative component
function Greeting({ name }) {
  return <h1>Hello, {name}</h1>;
}

<Greeting name="Suriya" />;
πŸ’‘ The component does not find or modify the h1 directly. Its output is determined by the input prop.
Key Takeaways
  • React encourages describing UI from state rather than manually mutating DOM nodes.
  • A component render should be treated as a pure calculation.
  • Understanding JavaScript is a prerequisite for understanding React well.
Knowledge Check

What is the central idea behind React's declarative model?

Components, Composition & Reuse

A React component is normally a function that returns React elements. Components become powerful when they are composed rather than built as large conditional blocks.

There are two common reuse mechanisms: props and composition. Props allow a parent to configure a child. Composition allows a parent to provide arbitrary React content, commonly through children or named props such as header, footer, or actions.

Prefer components that have a clear responsibility. A UserCard can render a user's presentation while receiving data and callbacks from its parent. It should not secretly fetch unrelated global data, manipulate another component's DOM, or contain every business rule in the application.

Component boundaries should follow change boundaries. If a section of UI has its own state, accessibility behavior, loading state, or reuse potential, it is a good candidate for a component.

Composition with children
function Card({ title, children }) {
  return (
    <section className="card">
      <h2>{title}</h2>
      <div>{children}</div>
    </section>
  );
}

function Profile() {
  return (
    <Card title="Profile">
      <p>Frontend engineer</p>
    </Card>
  );
}
πŸ’‘ The Card owns layout while the caller owns the content. This keeps the component reusable without adding dozens of specialized props.
Key Takeaways
  • Prefer composition when callers need flexible content.
  • Avoid giant components that own unrelated responsibilities.
  • Good boundaries isolate behavior, styling, state, and change.
Knowledge Check

What is a common benefit of component composition?

JSX: Syntax, Expressions & Transformation

JSX is syntax that lets you write a UI tree using JavaScript expressions embedded in markup-like syntax. JSX is not HTML and browsers do not execute JSX directly. A build tool or compiler transforms it into JavaScript expressions.

Use {} to embed JavaScript expressions. Expressions can produce strings, numbers, elements, arrays, or null. Statements such as if cannot be placed directly inside JSX braces, although conditional expressions, logical operators, helper functions, and variables can be used.

JSX also has JavaScript naming rules. For example, className maps to the DOM class property, and htmlFor represents the HTML for attribute. Custom components use capitalized names while intrinsic DOM elements use lowercase names.

Conditional JSX
function Status({ loading, error }) {
  if (loading) return <p>Loading…</p>;
  if (error) return <p role="alert">Failed to load.</p>;
  return <p>Ready</p>;
}
πŸ’‘ Early returns are often clearer than deeply nested ternaries when UI states become substantial.
Key Takeaways
  • JSX is JavaScript syntax transformed before it reaches the browser.
  • Only expressions can appear directly inside JSX braces.
  • Keep complex rendering decisions readable with variables or early returns.
Knowledge Check

Which value can be returned directly from a JSX expression?

Props, State & One-Way Data Flow

Props are inputs supplied by a parent. State is data owned by a component that can change over time. A component receives a new render whenever its relevant inputs or state change.

React encourages one-way data flow: data generally moves from parent to child through props. When a child needs to cause a parent-owned state change, the parent passes a callback. This makes ownership explicit and helps prevent multiple components from independently maintaining contradictory copies of the same data.

A common design mistake is duplicating derived data in state. If filteredProducts can be calculated from products and query, storing both creates synchronization problems. Store the source of truth and derive the rest during rendering when the calculation is cheap.

One-Way Data Flow
Lifting state up
function SearchPage() {
  const [query, setQuery] = useState("");

  return (
    <>
      <SearchBox query={query} onQueryChange={setQuery} />
      <Results query={query} />
    </>
  );
}
πŸ’‘ The parent owns the shared query because both SearchBox and Results need the same source of truth.
Key Takeaways
  • Props flow downward; events/callbacks communicate upward.
  • Keep one source of truth for shared state.
  • Prefer deriving data over duplicating it in state.
Knowledge Check

Where should shared state usually live?

Rendering, Purity & Strict Mode

A render is a calculation of the UI for the current props and state. React may call component functions more than once during development or as part of its rendering strategy. Therefore, rendering code should be pure: it should not make network requests, mutate external objects, subscribe to services, or perform other side effects.

React Strict Mode deliberately exposes certain unsafe patterns during development. Seeing a component render more than once in development is not evidence that production necessarily performs the same sequence. Treat render as repeatable and move side effects into appropriate effects or event handlers.

Purity does not mean an application cannot have side effects. It means side effects should happen at explicit boundaries where their lifecycle can be controlled.

Do not fight Strict Mode

If development exposes duplicate effects or renders, first ask whether your code is relying on an effect or render being called exactly once. Fix the lifecycle assumption rather than adding flags to hide it.

Key Takeaways
  • Render functions should be pure and repeatable.
  • Strict Mode is a development aid for finding unsafe assumptions.
  • Event handlers and effects are different side-effect boundaries.

Lists, Keys & Component Identity

When rendering a collection, each sibling element should have a stable key. Keys are not simply optimization hints: they help React identify which conceptual item corresponds to which previous item.

Use stable identifiers from your data whenever possible. An array index can be acceptable for truly static lists that never reorder, insert, delete, or change identity, but it becomes dangerous for dynamic lists. If a list item contains local state, using an index key can cause that state to appear to move to another item after reordering.

Keys are scoped to the surrounding list. They are not normally available as props.key inside the child component.

Stable keys
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
}
πŸ’‘ A database or domain identifier is preferable to the array index when items can be reordered or removed.
Key Takeaways
  • Keys describe identity among siblings.
  • Prefer stable domain IDs.
  • Changing a key intentionally resets component identity and its local state.
Knowledge Check

Why can array-index keys be problematic in a reorderable list?