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.
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.
function Greeting({ name }) {
return <h1>Hello, {name}</h1>;
}
<Greeting name="Suriya" />;- 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.
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.
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>
);
}- Prefer composition when callers need flexible content.
- Avoid giant components that own unrelated responsibilities.
- Good boundaries isolate behavior, styling, state, and change.
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.
function Status({ loading, error }) {
if (loading) return <p>Loadingβ¦</p>;
if (error) return <p role="alert">Failed to load.</p>;
return <p>Ready</p>;
}- 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.
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.
function SearchPage() {
const [query, setQuery] = useState("");
return (
<>
<SearchBox query={query} onQueryChange={setQuery} />
<Results query={query} />
</>
);
}- Props flow downward; events/callbacks communicate upward.
- Keep one source of truth for shared state.
- Prefer deriving data over duplicating it in state.
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.
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.
- 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.
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}- Keys describe identity among siblings.
- Prefer stable domain IDs.
- Changing a key intentionally resets component identity and its local state.
Why can array-index keys be problematic in a reorderable list?