Advanced Web Programming
React Components, JSX, Props and Composition
PGCP-AC
React expresses an interface as a tree of reusable components. Each render calculates a description from current props, state and context; React then commits the necessary host-environment changes. Understanding this declarative model prevents direct DOM habits from leaking into component design.
1. Describing an interface
React builds interfaces from components. A component describes the UI for its inputs and current state; React reconciles these descriptions with the rendered tree. JSX is syntax for describing elements, not an HTML string. JavaScript expressions appear inside braces and attributes use conventions such as className. A component name begins with an uppercase letter so JSX can distinguish it from a built-in element name.
Props carry data from a parent to a child. A child should treat received props as read-only and communicate intended changes through callbacks. Composition creates larger interfaces by combining smaller components. The children prop supports containment, such as a panel wrapping arbitrary content. This often models UI variation more naturally than a deep inheritance hierarchy.
2. Rendering decisions
Conditional rendering uses ordinary JavaScript decisions and expressions. Lists are commonly produced by mapping data to elements. Keys distinguish siblings across renders; stable identifiers are preferable when items may be inserted, deleted or reordered. A key need not be globally unique, but it must identify an item consistently within its sibling collection. A fragment groups siblings without introducing an extra DOM wrapper.
function Course({ name, children }) {
return <section><h2>{name}</h2>{children}</section>;
}
const view = <Course name="Java"><p>Object-oriented programming</p></Course>;
Function components and class components are both present in the course material. A class component implements a render method; function components return their descriptions directly. Rendering should remain pure: network requests, subscriptions and DOM synchronization belong in appropriate lifecycle or effect mechanisms. Escaped text interpolation differs from deliberately inserting raw HTML, which requires separate care.
3. Thinking in components
Begin with the data model and visual responsibilities. A bookshop page can contain SearchBox, BookList, BookRow and CartSummary. The list receives records; the row receives one record and an action callback; the summary receives totals. A component should not copy a prop into local state unless it deliberately wants an independent value, because that creates two sources that can disagree.
Conditional rendering can use an if statement, a conditional expression or logical composition. Be careful with count && <Badge />: when count is zero, the expression is zero rather than false, so an explicit Boolean condition is clearer. A list key identifies React's reconciliation relationship and is not automatically passed to the child as an ordinary prop. Pass the identifier separately when the child needs it for application logic.
4. Declarative rendering
Imperative DOM code describes a sequence of mutations: find an element, change text, add a class and remove a child. React components describe what the interface should be for the current inputs.
function Status({ online }) {
return <p className={online ? 'status online' : 'status offline'}>
{online ? 'Available' : 'Offline'}
</p>;
}
When online changes, React renders the component again, compares the new element description with the previous committed tree and updates the required host nodes. A render is calculation, not permission to mutate unrelated objects or perform arbitrary external work.
Components should be invoked through JSX, such as <Status online />, rather than called as ordinary functions. React controls component execution and uses that control to associate state and lifecycle behavior with tree positions.
5. JSX rules
JSX resembles HTML but is JavaScript syntax transformed into element creation calls. Important rules include:
- component names begin with an uppercase letter;
- JavaScript expressions appear inside
{}; - attributes commonly use JavaScript-oriented names such as
classNameandhtmlFor; - tags must be closed;
- a returned expression needs one root value, which may be a Fragment;
- event handlers receive functions, for example
onClick={handleClick}.
function CourseHeading({ code, title }) {
const label = `${code}: ${title}`;
return (
<header className="course-heading">
<h2>{label}</h2>
</header>
);
}
Braces switch from JSX markup into a JavaScript expression. Statements such as if cannot appear directly as expressions, but can run before the return. JSX comments use {/* comment */}.
6. Components and module boundaries
A function component receives one props object and returns renderable output. Components may share a file when small and closely related; reusable or independently changing components often deserve their own module.
export default function Avatar({ person, size = 48 }) {
return (
<img
src={person.imageUrl}
alt={person.name}
width={size}
height={size}
/>
);
}
Do not define component functions inside another component merely for visual grouping. A new component type is then created on every parent render, which can reset state below that position and complicate reconciliation. Define stable component types at module scope and pass data through props.
7. Props and one-way data flow
Props configure a child and are read-only snapshots for a render. A component must not modify a received object or array. When the parent owns a value, it also owns the operation that changes it and can pass a callback:
function CourseRow({ course, onSelect }) {
return (
<li>
<span>{course.title}</span>
<button type="button" onClick={() => onSelect(course.id)}>Open</button>
</li>
);
}
The child does not reach into parent variables. It reports an intention with the stable record identifier. The parent decides what selection means and whether state, navigation or analytics should change.
Destructuring props is syntax convenience. Default parameter values apply when a prop is missing or undefined, not when it is explicitly null.
8. Composition and children
Composition builds larger behavior from components without requiring inheritance.
function Panel({ title, actions, children }) {
return (
<section className="panel">
<header><h2>{title}</h2><div>{actions}</div></header>
<div className="panel__body">{children}</div>
</section>
);
}
<Panel title="Topic progress" actions={<button>Refresh</button>}>
<ProgressTable />
</Panel>
children is one composition slot; named props can provide additional slots. The panel controls structure and presentation while callers supply varying content. This keeps domain-independent layout reusable without a deep subclass hierarchy.
9. Conditional rendering
React uses ordinary JavaScript conditions:
function Result({ status, data }) {
if (status === 'loading') return <p>Loading…</p>;
if (status === 'error') return <p role="alert">Unable to load results.</p>;
if (data.length === 0) return <p>No results found.</p>;
return <ResultList items={data} />;
}
Use an early return for substantially different branches, a conditional expression for one compact choice and && for an optional value only when the left operand cannot produce unwanted display output. count && <Badge /> displays 0 when count is zero; count > 0 && <Badge /> expresses the intended boolean condition.
A component may return null, although a parent-level conditional often makes hidden behavior easier to locate.
10. Rendering lists and choosing keys
function CourseList({ courses, onSelect }) {
return (
<ul>
{courses.map(course => (
<CourseRow key={course.id} course={course} onSelect={onSelect} />
))}
</ul>
);
}
Keys tell React which sibling represents the same conceptual item between renders. A key should be unique among siblings, stable over time and derived from the data. Database IDs are suitable. Random values and timestamps change each render, destroying continuity. Array indexes are risky when items can be inserted, deleted, sorted or filtered because state may remain with a position rather than the intended record.
key is reconciliation metadata and is not passed as an ordinary prop. Pass id={course.id} separately when the child needs the identifier.
11. Fragments and structural HTML
A Fragment groups returned siblings without creating an extra DOM wrapper:
function Definition({ term, description }) {
return (
<>
<dt>{term}</dt>
<dd>{description}</dd>
</>
);
}
The short Fragment syntax cannot receive a key. Use the explicit Fragment form when mapping a keyed group. Avoid wrappers that produce invalid table, list or description-list structure merely to satisfy the single-root rule.
12. Render purity
A component should calculate the same JSX for the same props, state and context and should not mutate values created outside that render.
let nextNumber = 0;
function ImpureRow() {
nextNumber += 1; // changes external state during render
return <p>Row {nextNumber}</p>;
}
This result depends on call order. React may render more than once, restart work or render in another environment. Put user-triggered operations in event handlers. Use effects only to synchronize with external systems after commit, not for values that can be calculated during render.
Locally created values may be safely built during rendering because they did not exist before the calculation and are not shared external state.
13. Component identity and tree position
React associates state with a component type at a position in the rendered tree. Changing the type or key at that position can reset its state. Keeping the same component in the same position preserves its identity even when props change.
Keys are therefore also useful outside simple lists when two views at one position should be treated as distinct identities. Do not generate changing keys merely to silence a warning; that forces remounting and loses local state and DOM continuity.
14. Text safety and raw HTML
Ordinary string interpolation is escaped as text:
const comment = '<img src=x onerror=alert(1)>';
return <p>{comment}</p>;
The string is displayed rather than interpreted as an element. Deliberate raw HTML insertion uses a specially named API and must receive content trusted or sanitized under a suitable policy. React's normal escaping does not make unsafe URLs, authorization decisions or insecure backend data handling safe.
15. Accessibility in component APIs
Reusable components should make correct semantics easy. A button component should render a native button unless navigation is intended. An input component should provide a way to associate a visible label, help text and errors. Do not hide required semantics inside undocumented prop combinations.
Props spread onto a DOM element can unintentionally pass unsupported attributes or override critical handlers. Select and document public props, merge classes intentionally and keep accessible names stable.
16. Integrated course-card example
function CourseCard({ course, selected, onSelect, children }) {
return (
<article className={selected ? 'course-card selected' : 'course-card'}>
<h2>{course.title}</h2>
<p>{course.summary}</p>
{course.tags.length > 0 && (
<ul aria-label="Topics">
{course.tags.map(tag => <li key={tag}>{tag}</li>)}
</ul>
)}
{children}
<button type="button" onClick={() => onSelect(course.id)}>
{selected ? 'Selected' : `Select ${course.title}`}
</button>
</article>
);
}
The component receives read-only data, maps stable tag values to keyed siblings, uses an explicit boolean conditional, composes caller content and reports the selection intent upward.
17. Class components in existing material
Class components extend React.Component and implement render(). Props are available through this.props and state and lifecycle methods follow class APIs. They remain relevant in existing code and course history, although function components and Hooks are the common style for new React code.
Do not mix class and function mental models mechanically. A function component is not a class instance and has no this; Hooks follow their own rules, covered in later topics.
18. Practical considerations
- JSX is syntax for element descriptions, not an HTML string.
- Custom component names begin with uppercase letters.
- Props are read-only inputs for the current render.
- Children communicate upward through callbacks supplied by owners.
- Keys identify siblings and are not automatically passed as props.
- Random or index keys can destroy identity in changing lists.
count && elementcan render zero.- Rendering should be pure and free of uncontrolled side effects.
- Ordinary JSX string interpolation escapes markup.
- Composition is usually preferred for reusable visual containment.
Continue learning
Related notes
Put this topic into timed practice
Open mock tests when you want full-exam pacing, or keep drilling in practice mode.