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 startThis opens your app athttp://localhost:3000. - Clean Up:
Opensrc/App.jsand 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.csswith: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
Movieclass component. - Allow users to rate movies (1–5 stars) and update state.
- Include a
MovieFormclass 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
handleRatingin 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
handleChangeandhandleSubmitin the constructor. - Uses computed property names (
[e.target.name]) to update state dynamically. - Passes new movie data to the parent via
onAddMovieprop.
- Class component with state for form inputs (
- Features:
Step 3: Update the App Component
- Update
src/App.jsto 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
addMovieto update the movie list. - Uses
setStatewith a function to safely update state. - Renders
MovieFormand a list ofMoviecomponents 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, andMovieFormare all class components extendingReact.Component. - Props:
Moviereceivestitle,director, andratingas props;MovieFormreceivesonAddMovie. - State:
Appmanages themovieslist,Moviemanages individualrating, andMovieFormmanages form inputs. - Lifecycle Methods:
AppusescomponentDidMountandcomponentDidUpdateto log lifecycle events. - Event Handling: Methods like
handleRatingandhandleSubmitare bound in constructors to ensure correctthiscontext. - JSX: Each component uses JSX to define its UI, with dynamic rendering via props and state.
- ES6 Features:
- Class syntax and
superfor 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.statein the constructor and callsuper(props). - Use Functional Updates for State: Use
setStatewith 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.,
Moviedisplays and rates a movie). - Use PropTypes (Optional): Validate props with
PropTypesfor better debugging:import PropTypes from 'prop-types'; Movie.propTypes = { title: PropTypes.string.isRequired };
Common Class Component Pitfalls and Fixes
- Unbound Methods:
Problem: Event handlers losethiscontext, 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 incomponentDidUpdatecan 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 withoutkeyprops causes warnings.
Fix: Add uniquekeyprops (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
useStateanduseEffect. - Explore Lifecycle: Use
componentWillUnmountto 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!