Topic 3: React Components
📖 9 min read · 🎯 beginner · 🧭 Prerequisites: php-with-select-fetch, postman-configuration
Why this matters
Up until now, you've probably seen a big React file and thought — where do I even start? Here's the thing: React doesn't expect you to write everything in one place. It lets you break your UI into small, reusable pieces called components. Think of it like building with LEGO blocks — each block does one thing, and you snap them together to build something big. In this lesson, we'll cover how components work, how they talk to each other through props and state, how they respond to user actions, and when they come alive or get removed from the screen.
What You'll Learn
- Distinguish between functional components and class components and know when to use each
- Pass data between components using props
- Manage dynamic data inside a component using state
- Attach event handlers to user interactions
- Understand React's component lifecycle and its key methods
The Analogy
Think of React components like the modular rooms in a Lego house. Each room (component) is self-contained — it has its own walls, furniture, and purpose. You can snap a bedroom into one house and reuse the exact same blueprint in another house across town. You can even hand a room a nameplate (props) so it knows whose bedroom it is. And some rooms have a thermostat (state) that tracks and responds to changes happening inside them — when the temperature shifts, the room reacts immediately. The whole house (your app) is just rooms snapped together.
Chapter 1: Introduction to React Components
In React, components are the fundamental building blocks of any UI. Every button, form, header, and page section you see in a React app is a component. Components come in two main flavors:
- Functional components — plain JavaScript functions that accept props and return JSX
- Class components — ES6 classes that extend
React.Component, capable of holding state and lifecycle methods
Both types produce the same rendered output; the difference is in how they manage internal logic. Modern React heavily favors functional components (especially with Hooks), but class components are still found in many production codebases and are important to understand.
graph TD
App["App (root component)"]
App --> FunctionalGreeting
App --> ClassGreeting
App --> GreetingWithProps
App --> StatefulCounter
App --> ClickHandler
App --> LifecycleComponent
Chapter 2: Functional Components
Functional components are simple JavaScript functions that return JSX. They are typically used for presentational purposes — rendering UI based on whatever data they receive.
src/components/FunctionalGreeting.js:
import React from 'react';
function FunctionalGreeting() {
return <h1>Hello, Vizag!</h1>;
}
export default FunctionalGreeting;
Integrating FunctionalGreeting into App
The class plugged the new component into the root App component:
src/App.js:
import React from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
function App() {
return (
<div className="App">
<FunctionalGreeting />
</div>
);
}
export default App;
Rendering <FunctionalGreeting /> is identical to calling FunctionalGreeting() — React just uses the JSX tag syntax to keep things readable.
Chapter 3: Class Components
Class components are ES6 classes that extend React.Component. They must implement a render() method that returns JSX. They can hold their own state and hook into React's lifecycle methods — capabilities that originally made them the dominant pattern before Hooks arrived.
src/components/ClassGreeting.js:
import React, { Component } from 'react';
class ClassGreeting extends Component {
render() {
return <h1>Hello, Vizag from a Class Component!</h1>;
}
}
export default ClassGreeting;
Integrating ClassGreeting into App
src/App.js:
import React from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
import ClassGreeting from './components/ClassGreeting';
function App() {
return (
<div className="App">
<FunctionalGreeting />
<ClassGreeting />
</div>
);
}
export default App;
Both components now render their respective headings one after another in the browser.
Chapter 4: Props
Props (short for properties) are read-only attributes used to pass data from a parent component to a child component. They give components dynamic behavior without requiring the child to manage its own data. A component that receives props via its function argument can use them directly inside JSX with curly-brace interpolation.
src/components/GreetingWithProps.js:
import React from 'react';
function GreetingWithProps(props) {
return <h1>Hello, {props.name}!</h1>;
}
export default GreetingWithProps;
Integrating GreetingWithProps into App
The parent passes a name prop as a JSX attribute:
src/App.js:
import React from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
import ClassGreeting from './components/ClassGreeting';
import GreetingWithProps from './components/GreetingWithProps';
function App() {
return (
<div className="App">
<FunctionalGreeting />
<ClassGreeting />
<GreetingWithProps name="Vizag" />
</div>
);
}
export default App;
Changing name="Vizag" to name="Alice" would instantly update the rendered heading — no changes to GreetingWithProps required. That's the power of props.
Key rules about props:
- Props flow down — from parent to child only
- Props are read-only inside the receiving component — never mutate them
- Any JavaScript value can be a prop: strings, numbers, arrays, objects, functions, or even other components
Chapter 5: State
State is a built-in mechanism for storing data that can change over time inside a component. When state changes, React automatically re-renders the component to reflect the new data. In class components, state is initialized in the constructor and updated with this.setState().
src/components/StatefulCounter.js:
import React, { Component } from 'react';
class StatefulCounter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<h1>Count: {this.state.count}</h1>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
export default StatefulCounter;
Key mechanics at play here:
this.state = { count: 0 }sets the initial state in the constructorthis.setState(...)schedules a re-render with the new state value — never mutatethis.statedirectlyincrementis defined as a class field arrow function so thatthisis correctly bound without needing.bind(this)in the constructor
Integrating StatefulCounter into App
src/App.js:
import React from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
import ClassGreeting from './components/ClassGreeting';
import GreetingWithProps from './components/GreetingWithProps';
import StatefulCounter from './components/StatefulCounter';
function App() {
return (
<div className="App">
<FunctionalGreeting />
<ClassGreeting />
<GreetingWithProps name="Vizag" />
<StatefulCounter />
</div>
);
}
export default App;
Each click of the "Increment" button updates count, which triggers a re-render showing the new value.
Chapter 6: Handling Events
React components handle user interactions using event handlers — functions attached to JSX elements via props like onClick, onChange, onSubmit, and more. The syntax mirrors standard HTML events but uses camelCase naming.
src/components/ClickHandler.js:
import React, { Component } from 'react';
class ClickHandler extends Component {
handleClick = () => {
alert('Button was clicked!');
};
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
</div>
);
}
}
export default ClickHandler;
Notice that onClick={this.handleClick} passes the function reference — no parentheses. Writing onClick={this.handleClick()} would call the function immediately during render, not on click.
Integrating ClickHandler into App
src/App.js:
import React from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
import ClassGreeting from './components/ClassGreeting';
import GreetingWithProps from './components/GreetingWithProps';
import StatefulCounter from './components/StatefulCounter';
import ClickHandler from './components/ClickHandler';
function App() {
return (
<div className="App">
<FunctionalGreeting />
<ClassGreeting />
<GreetingWithProps name="Vizag" />
<StatefulCounter />
<ClickHandler />
</div>
);
}
export default App;
Chapter 7: Component Lifecycle
Every React class component goes through a predictable lifecycle: it mounts (appears in the DOM), optionally updates (when props or state change), and eventually unmounts (is removed from the DOM). React exposes lifecycle methods you can override to run code at each stage.
stateDiagram-v2
[*] --> Constructor
Constructor --> Render: initialize state
Render --> componentDidMount: first paint
componentDidMount --> Render: setState triggers update
Render --> componentDidUpdate: re-render complete
componentDidUpdate --> componentWillUnmount: component removed
componentWillUnmount --> [*]
src/components/LifecycleComponent.js:
import React, { Component } from 'react';
class LifecycleComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: null
};
console.log('Constructor');
}
componentDidMount() {
console.log('componentDidMount');
// Simulate an API call
setTimeout(() => {
this.setState({ data: 'Data fetched' });
}, 2000);
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate');
if (prevState.data !== this.state.data) {
console.log('Data has been updated');
}
}
componentWillUnmount() {
console.log('componentWillUnmount');
}
render() {
console.log('Render');
return (
<div>
<h1>Lifecycle Methods</h1>
<p>{this.state.data || 'Loading...'}</p>
</div>
);
}
}
export default LifecycleComponent;
What each method does:
| Method | When it runs | Common use |
|---|---|---|
constructor | Before first render | Initialize state, bind methods |
render | Every render (initial + updates) | Return JSX |
componentDidMount | After first render, DOM is ready | API calls, subscriptions, timers |
componentDidUpdate | After every re-render | React to prop/state changes |
componentWillUnmount | Before removal from DOM | Cleanup timers, cancel subscriptions |
Integrating LifecycleComponent into App
The class used useState in the root App to toggle LifecycleComponent on and off — which triggers componentWillUnmount when toggled off, demonstrating the full lifecycle:
src/App.js:
import React, { useState } from 'react';
import './App.css';
import FunctionalGreeting from './components/FunctionalGreeting';
import ClassGreeting from './components/ClassGreeting';
import GreetingWithProps from './components/GreetingWithProps';
import StatefulCounter from './components/StatefulCounter';
import ClickHandler from './components/ClickHandler';
import LifecycleComponent from './components/LifecycleComponent';
function App() {
const [showLifecycle, setShowLifecycle] = useState(true);
return (
<div className="App">
<FunctionalGreeting />
<ClassGreeting />
<GreetingWithProps name="Vizag" />
<StatefulCounter />
<ClickHandler />
{showLifecycle && <LifecycleComponent />}
<button onClick={() => setShowLifecycle(!showLifecycle)}>
Toggle Lifecycle Component
</button>
</div>
);
}
export default App;
Open your browser's DevTools console while clicking "Toggle Lifecycle Component" and watch the lifecycle messages fire in sequence: Constructor → Render → componentDidMount → (2s pause) → componentDidUpdate → componentWillUnmount.
🧪 Try It Yourself
Task: Build a TemperatureDisplay class component that:
- Initializes state with
{ tempC: 20 } - Renders the temperature in both Celsius and Fahrenheit (
F = C × 9/5 + 32) - Provides "Warmer" and "Cooler" buttons that increment/decrement
tempCby 1
Success criterion: Clicking "Warmer" should show 21°C / 69.8°F, clicking "Cooler" from 20 should show 19°C / 66.2°F. Both values update simultaneously with each click.
Starter snippet:
import React, { Component } from 'react';
class TemperatureDisplay extends Component {
constructor(props) {
super(props);
this.state = { tempC: 20 };
}
// Add warmer and cooler handlers here
render() {
const { tempC } = this.state;
const tempF = /* your formula here */;
return (
<div>
<h2>{tempC}°C / {tempF}°F</h2>
{/* Add your buttons here */}
</div>
);
}
}
export default TemperatureDisplay;
🔍 Checkpoint Quiz
Q1. What is the key structural difference between a functional component and a class component in React?
A) Functional components cannot receive props; class components can
B) Functional components are plain JS functions returning JSX; class components extend React.Component and require a render() method
C) Class components are faster at rendering than functional components
D) Functional components can only be used once per application
Q2. Given the following code, what will the browser display immediately after mounting, and what will it display after 2 seconds?
class Mystery extends Component {
constructor(props) {
super(props);
this.state = { message: null };
}
componentDidMount() {
setTimeout(() => this.setState({ message: 'Ready!' }), 2000);
}
render() {
return <p>{this.state.message || 'Loading...'}</p>;
}
}
A) "Ready!" immediately, then nothing after 2 seconds
B) Nothing at all — state is null so nothing renders
C) "Loading..." immediately, then "Ready!" after 2 seconds
D) An error, because null cannot be used as initial state
Q3. What is wrong with the following event handler attachment?
<button onClick={this.handleClick()}>Submit</button>
A) handleClick should be an arrow function
B) The parentheses cause handleClick to be called immediately during render, not on click
C) onClick is not a valid React event prop
D) Nothing is wrong — this is correct React syntax
Q4. You're building a UserCard component that should display a user's name and avatar passed in from a parent. The data never changes inside UserCard itself. Should you use props or state for this, and why?
A1. B — Functional components are plain JS functions; class components extend React.Component and use a render() method. Both can receive props; the difference is in structure and how they handle internal logic.
A2. C — On mount, this.state.message is null, so the fallback 'Loading...' renders. After 2 seconds, setState fires, triggering a re-render that shows 'Ready!'.
A3. B — Writing onClick={this.handleClick()} invokes the function during the render phase and passes its return value (likely undefined) as the handler. The correct form is onClick={this.handleClick} — a reference, not a call.
A4. Use props. The data originates in the parent and is read-only inside UserCard. State is for data that the component itself owns and changes. Passing the name and avatar as props lets the parent control the data while keeping UserCard a pure, reusable display component.
🪞 Recap
- React components are the building blocks of any React UI — each one is an isolated, reusable piece of the interface
- Functional components are plain JavaScript functions that return JSX; class components extend
React.Componentand require arender()method - Props pass read-only data from parent to child, making components dynamic and reusable
- State holds data that belongs to and changes within a component; calling
this.setState()schedules a re-render - Event handlers like
onClickconnect user interactions to component logic — always pass a function reference, never a call - Class components expose lifecycle methods (
componentDidMount,componentDidUpdate,componentWillUnmount) to run code at specific moments in a component's life
📚 Further Reading
- React Official Docs — Components and Props — the source of truth on props and component fundamentals
- React Official Docs — State: A Component's Memory — deep dive into how state works and when to use it
- React Legacy Docs — Class Component Lifecycle — complete reference for all class lifecycle methods
- ⬅️ Previous: Postman Configuration
- ➡️ Next: Windows Installation