React Class Components
24 April 2025 | Category: React Js
Welcome to this beginner-friendly tutorial on React Class Components! Class components are a traditional way to build React applications, using ES6 classes to create reusable UI elements with state and lifecycle methods. While functional components with hooks are now preferred, class components are still relevant for maintaining legacy code or specific use cases. In this guide, you’ll learn how to create class components, manage state and props, use lifecycle methods, and handle events. We’ll build a Movie Rating App that displays movies and allows users to rate them. By the end, you’ll understand class components and be ready to work with them in React.
What are Class Components?
A React class component is an ES6 class that extends React.Component
and includes a render
method to return JSX, defining the component’s UI. Class components can manage state, handle props, and use lifecycle methods to control behavior at different stages of a component’s life (e.g., mounting, updating, unmounting).
Why Learn Class Components?
- Legacy Code: Many older React projects use class components, so understanding them is essential for maintenance.
- Lifecycle Methods: Class components provide fine-grained control over a component’s lifecycle, useful for specific scenarios.
- Foundation: Learning class components deepens your understanding of React’s core concepts.
- Transition to Hooks: Knowing class components helps you appreciate why hooks were introduced.
Prerequisites
Before starting, you should have:
- Basic knowledge of React (JSX, props) and JavaScript (ES6 classes, objects).
- Node.js and npm installed (download from nodejs.org).
- A code editor like Visual Studio Code.
- A terminal for running commands.
We’ll use Create React App to set up our project, which supports JSX, ES6, and React 19 out of the box.
Key Class Component Concepts
Let’s explore the core features of class components.
1. Class Component Syntax
A class component is defined as an ES6 class that extends React.Component
and implements a render
method.
Example:
import React from 'react';
class Welcome extends React.Component {
render() {
return <h1>Hello, React!</h1>;
}
}
2. Props
Props are read-only inputs passed to a component, accessed via this.props
.
Example:
class Movie extends React.Component {
render() {
return <h2>{this.props.title}</h2>;
}
}
// Usage
<Movie title="Inception" />
3. State
State is a component’s internal data, managed via this.state
and updated with this.setState
.
Example:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Increment
</button>
</div>
);
}
}
4. Lifecycle Methods
Lifecycle methods are special methods that run at specific points in a component’s life:
- Mounting:
constructor
,componentDidMount
- Updating:
componentDidUpdate
- Unmounting:
componentWillUnmount
Example:
class Timer extends React.Component {
componentDidMount() {
console.log('Component mounted!');
}
render() {
return <p>Timer Component</p>;
}
}
5. Event Handling
Event handlers are methods bound to this
in the constructor to ensure proper context.
Example:
class Button extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
alert('Button clicked!');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
Setting Up the Project
Let’s create a React app to build our Movie Rating App, which will use class components to display and rate movies.
- Create a New React App:
Open your terminal and run:npx create-react-app movie-rating-app cd movie-rating-app
- Start the Development Server:
npm start
This opens your app athttp://localhost:3000
. - Clean Up:
Opensrc/App.js
and replace its content with:import React from 'react'; class App extends React.Component { render() { return ( <div> <h1>Movie Rating App</h1> </div> ); } } export default App;
Deletesrc/App.css
,src/logo.svg
, and updatesrc/index.css
with:body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } h1 { color: #333; }
Building the Movie Rating App
Our Movie Rating App will:
- Display a list of movies using a
Movie
class component. - Allow users to rate movies (1–5 stars) and update state.
- Include a
MovieForm
class component to add new movies. - Use lifecycle methods to log component updates.
Step 1: Create the Movie Component
- In
src
, create a file namedMovie.js
:import React from 'react'; class Movie extends React.Component { constructor(props) { super(props); this.state = { rating: props.rating || 0 }; this.handleRating = this.handleRating.bind(this); } handleRating(newRating) { this.setState({ rating: newRating }); } render() { const { title, director } = this.props; const { rating } = this.state; return ( <div style={{ backgroundColor: '#fff', padding: '15px', margin: '10px 0', borderRadius: '8px', boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', maxWidth: '400px' }}> <h3>{title}</h3> <p>Director: {director}</p> <p>Rating: {rating} / 5</p> <div> {[1, 2, 3, 4, 5].map((star) => ( <button key={star} onClick={() => this.handleRating(star)} style={{ backgroundColor: star <= rating ? '#ffc107' : '#e0e0e0', border: 'none', padding: '5px', margin: '2px', borderRadius: '4px' }} > ★ </button> ))} </div> </div> ); } } export default Movie;
- Features:
- Class component with state (
rating
) initialized from props. - Binds
handleRating
in the constructor to update state. - Renders movie details and a star-rating system using JSX.
- Destructures props and state for cleaner code.
- Class component with state (
- Features:
Step 2: Create the MovieForm Component
- In
src
, create a file namedMovieForm.js
:import React from 'react'; class MovieForm extends React.Component { constructor(props) { super(props); this.state = { title: '', director: '' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(e) { this.setState({ [e.target.name]: e.target.value }); } handleSubmit(e) { e.preventDefault(); const { title, director } = this.state; if (title.trim() && director.trim()) { this.props.onAddMovie({ title, director, rating: 0 }); this.setState({ title: '', director: '' }); } } render() { const { title, director } = this.state; return ( <form onSubmit={this.handleSubmit} style={{ marginBottom: '20px' }}> <div style={{ marginBottom: '10px' }}> <input type="text" name="title" value={title} onChange={this.handleChange} placeholder="Movie title" style={{ padding: '8px', width: '200px', borderRadius: '4px' }} /> </div> <div style={{ marginBottom: '10px' }}> <input type="text" name="director" value={director} onChange={this.handleChange} placeholder="Director" style={{ padding: '8px', width: '200px', borderRadius: '4px' }} /> </div> <button type="submit" style={{ padding: '8px 16px', backgroundColor: '#28a745', color: '#fff', border: 'none', borderRadius: '4px' }} > Add Movie </button> </form> ); } } export default MovieForm;
- Features:
- Class component with state for form inputs (
title
,director
). - Binds
handleChange
andhandleSubmit
in the constructor. - Uses computed property names (
[e.target.name]
) to update state dynamically. - Passes new movie data to the parent via
onAddMovie
prop.
- Class component with state for form inputs (
- Features:
Step 3: Update the App Component
- Update
src/App.js
to manage the movie list and use lifecycle methods:import React from 'react'; import Movie from './Movie'; import MovieForm from './MovieForm'; class App extends React.Component { constructor(props) { super(props); this.state = { movies: [ { title: 'Inception', director: 'Christopher Nolan', rating: 4 }, { title: 'The Matrix', director: 'Wachowski Sisters', rating: 3 } ] }; this.addMovie = this.addMovie.bind(this); } componentDidMount() { console.log('App mounted with', this.state.movies.length, 'movies'); } componentDidUpdate(prevProps, prevState) { if (prevState.movies.length !== this.state.movies.length) { console.log('Movie list updated:', this.state.movies); } } addMovie(newMovie) { this.setState((prevState) => ({ movies: [...prevState.movies, newMovie] })); } render() { const { movies } = this.state; return ( <div> <h1>Movie Rating App</h1> <MovieForm onAddMovie={this.addMovie} /> <div> {movies.length > 0 ? ( movies.map((movie, index) => ( <Movie key={index} title={movie.title} director={movie.director} rating={movie.rating} /> )) ) : ( <p>No movies yet. Add one!</p> )} </div> </div> ); } } export default App;
- Features:
- Class component with state (
movies
) and lifecycle methods (componentDidMount
,componentDidUpdate
). - Binds
addMovie
to update the movie list. - Uses
setState
with a function to safely update state. - Renders
MovieForm
and a list ofMovie
components with conditional JSX.
- Class component with state (
- Features:
Step 4: Test the App
- Save all files and ensure the development server is running (
npm start
). - Open
http://localhost:3000
. You should see:- Two initial movie cards for “Inception” and “The Matrix” with star ratings.
- A form to add new movies with title and director fields.
- The ability to rate movies by clicking stars (1–5).
- Try adding a movie (e.g., “Dune” by “Denis Villeneuve”) and rating it. Check the console for lifecycle method logs.
Understanding the Code
Let’s recap how class components power our Movie Rating App:
- Class Components:
App
,Movie
, andMovieForm
are all class components extendingReact.Component
. - Props:
Movie
receivestitle
,director
, andrating
as props;MovieForm
receivesonAddMovie
. - State:
App
manages themovies
list,Movie
manages individualrating
, andMovieForm
manages form inputs. - Lifecycle Methods:
App
usescomponentDidMount
andcomponentDidUpdate
to log lifecycle events. - Event Handling: Methods like
handleRating
andhandleSubmit
are bound in constructors to ensure correctthis
context. - JSX: Each component uses JSX to define its UI, with dynamic rendering via props and state.
- ES6 Features:
- Class syntax and
super
for inheritance. - Destructuring (
{ title, director }
). - Arrow functions in event handlers (e.g.,
onClick={() => this.handleRating(star)}
). - Spread operator (
[...prevState.movies, newMovie]
).
- Class syntax and
Best Practices for Class Components
- Bind Methods in Constructor: Bind event handlers in the constructor to avoid performance issues:
constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); }
- Initialize State Properly: Define
this.state
in the constructor and callsuper(props)
. - Use Functional Updates for State: Use
setState
with a function when updating state based on previous state:this.setState((prevState) => ({ count: prevState.count + 1 }));
- Avoid Overusing Lifecycle Methods: Only use lifecycle methods for necessary side effects (e.g., fetching data in
componentDidMount
). - Keep Components Focused: Each component should handle one responsibility (e.g.,
Movie
displays and rates a movie). - Use PropTypes (Optional): Validate props with
PropTypes
for better debugging:import PropTypes from 'prop-types'; Movie.propTypes = { title: PropTypes.string.isRequired };
Common Class Component Pitfalls and Fixes
- Unbound Methods:
Problem: Event handlers losethis
context, causing errors (e.g.,this.setState is not a function
).
Fix: Bind methods in the constructor or use arrow functions:handleClick = () => { this.setState({ ... }); };
- State Mutations:
Problem: Directly modifyingthis.state
(e.g.,this.state.count++
) doesn’t trigger re-renders.
Fix: Always usethis.setState
. - Lifecycle Overuse:
Problem: Adding complex logic incomponentDidUpdate
can cause infinite loops.
Fix: Add conditions to prevent unnecessary updates:componentDidUpdate(prevProps) { if (prevProps.value !== this.props.value) { // Update logic } }
- Missing Keys in Lists:
Problem: Rendering lists withoutkey
props causes warnings.
Fix: Add uniquekey
props (e.g.,<Movie key={id} />
).
Functional Components vs. Class Components
While this tutorial focuses on class components, modern React favors functional components with hooks. Here’s a quick comparison:
- Syntax: Functional components are simpler (functions vs. classes).
- State: Functional components use
useState
/useReducer
; class components usethis.state
/this.setState
. - Lifecycle: Functional components use
useEffect
; class components usecomponentDidMount
, etc. - Use Case: Use functional components for new code; use class components for legacy code or specific lifecycle needs.
Example (Functional Equivalent of Movie):
const Movie = ({ title, director, rating: initialRating }) => {
const [rating, setRating] = React.useState(initialRating || 0);
return (
<div>
<h3>{title}</h3>
<p>Director: {director}</p>
<p>Rating: {rating} / 5</p>
<div>
{[1, 2, 3, 4, 5].map((star) => (
<button key={star} onClick={() => setRating(star)}>
★
</button>
))}
</div>
</div>
);
};
What’s Next?
You’ve built a Movie Rating App using React class components! Here are some next steps:
- Add Features: Implement a button to delete movies or filter by rating.
- Learn Hooks: Transition to functional components with
useState
anduseEffect
. - Explore Lifecycle: Use
componentWillUnmount
to clean up resources (e.g., timers). - Build Another App: Create a task tracker or product catalog with class components.
Practice Challenge
Add a “Reset Rating” button to the Movie
component that sets the rating back to 0. Use setState
and bind the handler in the constructor.
Resources
- React Documentation: Class Components
- React Documentation: State and Lifecycle
- MDN: ES6 Classes
- Create React App Guide
Congratulations on mastering React class components! You’re now equipped to work with legacy React code and understand component lifecycles. Keep practicing and happy coding!