Components Structure and Usage

Introduction

React offers two primary ways of defining components: functional components and class components. While functional components have become the modern standard after the introduction of Hooks in React 16.8, class components still hold significant importance. Many existing projects are built with class components, and understanding them is crucial for developers who may need to maintain legacy codebases, work with older libraries, or fully grasp React’s evolution.

In this post, we will explore class components in detail: their structure, how they work, lifecycle methods, handling state, props, event handling, and best practices. By the end, you will have a solid understanding of how to create and use class components in React.


What are Class Components?

Class components in React are ES6 classes that extend from React.Component. They must include a render() method that returns JSX, which defines the UI.

Key Features of Class Components

  1. Defined as ES6 classes.
  2. Must extend React.Component or React.PureComponent.
  3. Require a render() method that outputs JSX.
  4. Have access to state (internal data).
  5. Can use lifecycle methods like componentDidMount, componentDidUpdate, etc.

Basic Structure of a Class Component

Let’s look at the simplest class component in React:

import React from 'react';

class Welcome extends React.Component {
  render() {
return <h1>Hello, {this.props.name}</h1>;
} } export default Welcome;

Explanation

  • class Welcome extends React.Component defines a new class that inherits from React.Component.
  • render() is required and returns the JSX UI.
  • this.props is used to access props passed from the parent.

Class Component vs Functional Component

Functional Component

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

Class Component

class Welcome extends React.Component {
  render() {
return &lt;h1&gt;Hello, {this.props.name}&lt;/h1&gt;;
} }

Both achieve the same result, but the class component gives access to additional features such as state and lifecycle methods (without Hooks).


Props in Class Components

Props in class components work the same way as in functional components, but they are accessed using this.props.

Example

class Greeting extends React.Component {
  render() {
return &lt;h2&gt;Good Morning, {this.props.username}!&lt;/h2&gt;;
} } function App() { return (
&lt;div&gt;
  &lt;Greeting username="Alice" /&gt;
  &lt;Greeting username="Bob" /&gt;
&lt;/div&gt;
); }

Output

Good Morning, Alice!
Good Morning, Bob!

State in Class Components

State is one of the most powerful features in class components. Unlike props, which are immutable, state is local to the component and can change over time.

Declaring State

In class components, state is initialized inside the constructor.

class Counter extends React.Component {
  constructor(props) {
super(props);
this.state = { count: 0 };
} render() {
return &lt;h1&gt;Count: {this.state.count}&lt;/h1&gt;;
} }

Updating State

You should never modify state directly. Instead, use this.setState().

Example

class Counter extends React.Component {
  constructor(props) {
super(props);
this.state = { count: 0 };
} increment = () => {
this.setState({ count: this.state.count + 1 });
}; render() {
return (
  &lt;div&gt;
    &lt;h1&gt;Count: {this.state.count}&lt;/h1&gt;
    &lt;button onClick={this.increment}&gt;Increment&lt;/button&gt;
  &lt;/div&gt;
);
} }

Each time the button is clicked, the state updates and the component re-renders.


Handling Events in Class Components

Event handling in class components requires binding methods to the component’s context (this). Without binding, this would be undefined inside the event handler.

Example without Binding (Error)

class ButtonDemo extends React.Component {
  constructor(props) {
super(props);
this.state = { message: "Click the button" };
} handleClick() {
this.setState({ message: "Button Clicked!" });
} render() {
return (
  &lt;div&gt;
    &lt;h2&gt;{this.state.message}&lt;/h2&gt;
    &lt;button onClick={this.handleClick}&gt;Click Me&lt;/button&gt;
  &lt;/div&gt;
);
} }

This code will throw an error because this is not bound.


Correct Approach – Binding in Constructor

class ButtonDemo extends React.Component {
  constructor(props) {
super(props);
this.state = { message: "Click the button" };
this.handleClick = this.handleClick.bind(this);
} handleClick() {
this.setState({ message: "Button Clicked!" });
} render() {
return (
  &lt;div&gt;
    &lt;h2&gt;{this.state.message}&lt;/h2&gt;
    &lt;button onClick={this.handleClick}&gt;Click Me&lt;/button&gt;
  &lt;/div&gt;
);
} }

Alternative Approach – Arrow Functions

Using arrow functions automatically binds this.

class ButtonDemo extends React.Component {
  state = { message: "Click the button" };

  handleClick = () => {
this.setState({ message: "Button Clicked!" });
}; render() {
return (
  &lt;div&gt;
    &lt;h2&gt;{this.state.message}&lt;/h2&gt;
    &lt;button onClick={this.handleClick}&gt;Click Me&lt;/button&gt;
  &lt;/div&gt;
);
} }

Lifecycle Methods

One of the key reasons class components are important is lifecycle methods. These methods allow you to hook into different phases of a component’s life.

Three Main Phases

  1. Mounting – When the component is inserted into the DOM.
  2. Updating – When state or props change.
  3. Unmounting – When the component is removed.

Common Lifecycle Methods

  • constructor() – Initializes state.
  • componentDidMount() – Runs after the component mounts.
  • componentDidUpdate() – Runs after updates.
  • componentWillUnmount() – Runs before unmounting.

Example: Using Lifecycle Methods

class LifecycleDemo extends React.Component {
  constructor(props) {
super(props);
this.state = { message: "Hello World!" };
console.log("Constructor called");
} componentDidMount() {
console.log("Component Mounted");
} componentDidUpdate() {
console.log("Component Updated");
} componentWillUnmount() {
console.log("Component Will Unmount");
} changeMessage = () => {
this.setState({ message: "Message Updated!" });
}; render() {
return (
  &lt;div&gt;
    &lt;h2&gt;{this.state.message}&lt;/h2&gt;
    &lt;button onClick={this.changeMessage}&gt;Change&lt;/button&gt;
  &lt;/div&gt;
);
} }

When rendered, this component logs messages during its lifecycle.


Conditional Rendering in Class Components

Conditional rendering decides what UI to show based on state or props.

class LoginControl extends React.Component {
  constructor(props) {
super(props);
this.state = { isLoggedIn: false };
} toggleLogin = () => {
this.setState({ isLoggedIn: !this.state.isLoggedIn });
}; render() {
return (
  &lt;div&gt;
    {this.state.isLoggedIn ? &lt;h1&gt;Welcome Back!&lt;/h1&gt; : &lt;h1&gt;Please Login&lt;/h1&gt;}
    &lt;button onClick={this.toggleLogin}&gt;
      {this.state.isLoggedIn ? "Logout" : "Login"}
    &lt;/button&gt;
  &lt;/div&gt;
);
} }

Rendering Lists in Class Components

class ListDemo extends React.Component {
  render() {
const items = &#91;"Apple", "Banana", "Cherry"];
return (
  &lt;ul&gt;
    {items.map((item, index) =&gt; (
      &lt;li key={index}&gt;{item}&lt;/li&gt;
    ))}
  &lt;/ul&gt;
);
} }

Real-World Example – Todo App with Class Components

class TodoApp extends React.Component {
  constructor(props) {
super(props);
this.state = { todos: &#91;], input: "" };
} handleChange = (e) => {
this.setState({ input: e.target.value });
}; addTodo = () => {
if (this.state.input.trim() !== "") {
  this.setState((prevState) =&gt; ({
    todos: &#91;...prevState.todos, prevState.input],
    input: "",
  }));
}
}; render() {
return (
  &lt;div&gt;
    &lt;h1&gt;Todo List&lt;/h1&gt;
    &lt;input
      type="text"
      value={this.state.input}
      onChange={this.handleChange}
    /&gt;
    &lt;button onClick={this.addTodo}&gt;Add&lt;/button&gt;
    &lt;ul&gt;
      {this.state.todos.map((todo, index) =&gt; (
        &lt;li key={index}&gt;{todo}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  &lt;/div&gt;
);
} }

This example demonstrates props, state, event handling, and rendering lists inside a class component.


Best Practices for Class Components

  1. Use functional components with Hooks for new projects but know class components for legacy support.
  2. Keep components small and focused on a single task.
  3. Always bind event handlers or use arrow functions.
  4. Use setState correctly and avoid direct state mutations.
  5. Use lifecycle methods for data fetching, subscriptions, and cleanup.
  6. Split complex UI into child components for reusability.

When to Use Class Components Today

Even though functional components dominate modern React development, class components are still useful in situations such as:

  • Working with older React codebases.
  • Understanding lifecycle methods for interviews and legacy projects.
  • Reading React documentation before Hooks were introduced.
  • Using some third-party libraries that rely on class components.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *