DEV Community

Darshan Vasani
Darshan Vasani Subscriber

Posted on

React Class Component

πŸš€ React Class Component & super(props) Full Guide

πŸ“˜ What Is super(props) and Why It Matters?

πŸ‘Ά In JavaScript Classes:

  • super() refers to the parent class's constructor.
  • In React, your class component extends from React.Component, so super() refers to React.Component.

🧠 Why Use super(props)?

βœ… To correctly initialize this.props in the constructor of a class component.

πŸ”Ž Without super(props):

constructor(props) {
  super(); // ❌ Missing props
  console.log(this.props); // ❌ undefined
}
Enter fullscreen mode Exit fullscreen mode

βœ… With super(props):

constructor(props) {
  super(props); // βœ…
  console.log(this.props); // βœ… accessible
}
Enter fullscreen mode Exit fullscreen mode

πŸ“’ Without it, you'll see errors like:

❌ "Must call super constructor in derived class before accessing 'this'"


πŸ“¦ Real-World Example (Your Code)

1️⃣ UserClass β€” Class Component

// πŸ“¦ import React
import React from 'react';

// πŸ‘¨β€πŸ« Class Component
class UserClass extends React.Component {
    constructor(props) {
        super(props); // βœ… Required to access this.props inside constructor
    }

    render() {
        return (
            <div className="user-card">
                <h1>πŸ‘€ User: {this.props.name}</h1>
                <h2>πŸ§‘ Name : Darshan Vasani</h2>
                <h2>πŸŽ‚ Age : 22</h2>
                <h2>πŸ“ Location : Surat, Gujarat</h2>
            </div>
        );
    }
}

export default UserClass;
Enter fullscreen mode Exit fullscreen mode

2️⃣ User β€” Functional Component (⚠️ Doesn't need super(props))

// πŸ“¦ import React
import React from 'react';

// πŸ”§ Function-based component
const User = ({ name }) => {
  return (
    <div className="user-card">
      <h1>πŸ‘€ User: {name}</h1>
      <h2>πŸ§‘ Name : Darshan Vasani</h2>
      <h2>πŸŽ‚ Age : 22</h2>
      <h2>πŸ“ Location : Surat, Gujarat</h2>
    </div>
  );
}

export default User;
Enter fullscreen mode Exit fullscreen mode

3️⃣ About β€” How You Use Both Components

import User from "./User";
import UserClass from "./UserClass";

const About = () => {
    return (
        <div className="about-page">
            <h1>ℹ️ About</h1>
            <p>This is the about page of our application.</p>

            {/* πŸ”§ Function Component */}
            <User name={"Darshan Vasani From Function"} />

            {/* πŸ—οΈ Class Component */}
            <UserClass name={"Darshan Vasani From Class"} />
        </div>
    );
};

export default About;
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Summary Table

🧩 Concept πŸ” Why Needed?
super() Calls React.Component constructor (required in child class)
super(props) Initializes this.props, lets you use this.props in constructor
Omit props ❌ this.props will be undefined, errors on usage
Functional Comp βœ… Doesn't need super() or constructor

βœ… Best Practices

πŸ”Ή Always use super(props) in a constructor of class-based components if you need this.props.

πŸ”Ή In modern React, functional components + hooks are preferred over class components β€” but class components still show up in many legacy codebases.


πŸ”š Final Thought:

🎯 If you're learning React or working with legacy apps, understanding super(props) is essential. But for new projects, prefer functional components with hooks β€” they’re simpler, cleaner, and more powerful!


🧠 React useEffect() vs. Class Lifecycle Methods

πŸ“Œ 1. useEffect(() => { ... }, [deps]) = Similar but not same as componentDidUpdate()

They behave similarly, but they are not the same! Let's break it down πŸ”


🟨 useEffect (Functional Component)

import React, { useEffect, useState } from "react";

const Counter = () => {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);

  // βœ… useEffect triggers only when count1 or count2 changes
  useEffect(() => {
    console.log("πŸ” Either count1 or count2 changed!");
  }, [count1, count2]);

  return (
    <div>
      <h2>Count1: {count1}</h2>
      <h2>Count2: {count2}</h2>
      <button onClick={() => setCount1(count1 + 1)}>βž• Count1</button>
      <button onClick={() => setCount2(count2 + 1)}>βž• Count2</button>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

🟩 componentDidUpdate() (Class Component)

import React from "react";

class CounterClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count1: 0,
      count2: 0,
    };
  }

  // βœ… Runs after every render β€” we must manually check what changed
  componentDidUpdate(prevProps, prevState) {
    if (
      prevState.count1 !== this.state.count1 ||
      prevState.count2 !== this.state.count2
    ) {
      console.log("πŸ” Either count1 or count2 changed (class version)!");
    }
  }

  render() {
    const { count1, count2 } = this.state;
    return (
      <div>
        <h2>Count1: {count1}</h2>
        <h2>Count2: {count2}</h2>
        <button onClick={() => this.setState({ count1: count1 + 1 })}>βž• Count1</button>
        <button onClick={() => this.setState({ count2: count2 + 1 })}>βž• Count2</button>
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ” Behavior Comparison Chart

Feature useEffect (Functional) componentDidUpdate (Class)
Triggered after render? βœ… Yes βœ… Yes
Automatically checks changes? βœ… Yes (via [deps]) ❌ No (you check manually using prevState)
Initial render runs? ❌ No (if deps exist) ❌ No (not on first render)
Manual check needed? ❌ No βœ… Yes
Cleanup function supported? βœ… Yes (return () => {}) βœ… Use componentWillUnmount()
Simpler to read? βœ… Yes ❌ Slightly more verbose

❗ Important: Not the Same!

useEffect(() => {}, [count1, count2]) and componentDidUpdate() are similar in purpose but not identical in behavior.

For example:

πŸ”Ή useEffect() won’t run on first render if you provide dependencies.
πŸ”Ή componentDidUpdate() also doesn't run on first render β€” but it requires manual checks.


useEffect(() => {
  // logic
}, [count1, count2]);
Enter fullscreen mode Exit fullscreen mode

is replicated in Class-based components.


βœ… Hook Behavior Recap:

useEffect(() => {
  // runs when either count1 or count2 changes
}, [count1, count2]);
Enter fullscreen mode Exit fullscreen mode

This means: "Run this effect whenever count1 or count2 changes."


πŸ” Equivalent in Class Components:

In class components, you use componentDidUpdate(prevProps, prevState) to detect changes in specific state values:

βœ… Example (Class-Based)

import React from 'react';

class CounterComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count1: 0,
      count2: 0,
    };
  }

  componentDidUpdate(prevProps, prevState) {
    // Check if count1 or count2 has changed
    if (
      prevState.count1 !== this.state.count1 ||
      prevState.count2 !== this.state.count2
    ) {
      console.log("πŸ”„ count1 or count2 changed!");
      // Perform your logic here...
    }
  }

  render() {
    const { count1, count2 } = this.state;
    return (
      <div>
        <h2>Count1: {count1}</h2>
        <h2>Count2: {count2}</h2>
        <button onClick={() => this.setState({ count1: count1 + 1 })}>
          βž• Increase Count1
        </button>
        <button onClick={() => this.setState({ count2: count2 + 1 })}>
          βž• Increase Count2
        </button>
      </div>
    );
  }
}

export default CounterComponent;
Enter fullscreen mode Exit fullscreen mode

🧠 Summary

  • βœ… Use useEffect(() => {...}, [deps]) to track specific state/prop changes in functional components
  • 🧠 In class components, use componentDidUpdate(prevProps, prevState) and compare values manually

* ⚠️ Don’t assume they are 1:1 interchangeable β€” their timing and cleanup behavior differ

πŸ” Summary Table

Hook Version Class-Based Equivalent
useEffect(() => {}, []) componentDidMount()
useEffect(() => {}, [var1, var2]) componentDidUpdate(prevProps, prevState) with condition
useEffect(() => { return () => {} }) componentWillUnmount()

πŸšͺ componentWillUnmount() in React Class Components

βœ… It is a lifecycle method that runs just before a component is removed (unmounted) from the DOM.


βœ… Use Cases of componentWillUnmount() πŸ”₯

Use Case Why it's needed
πŸ”„ Clear timers (setInterval, setTimeout) Avoid memory leaks & unwanted behavior
🎧 Remove event listeners Avoid duplicate listeners
πŸ“‘ Cancel API requests Prevent state updates after unmount
🎯 Cleanup animations/subscriptions Prevent side effects from inactive components

🚫 Problem without componentWillUnmount()

When you don't clear setInterval β†’ The callback continues to run even after the component is gone.
This causes:

  • ❌ Memory leaks
  • ❌ Errors like β€œCan't update state on unmounted component”
  • ❌ Extra API calls or rendering

βœ… Example: setInterval without cleanup (❌ Problem)

class TimerComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  componentDidMount() {
    // πŸ” Start a timer
    this.interval = setInterval(() => {
      this.setState({ seconds: this.state.seconds + 1 });
    }, 1000);
  }

  render() {
    return <h2>⏱️ Timer: {this.state.seconds}s</h2>;
  }
}

// ⚠️ If this component is unmounted, interval continues! Memory leak 😱
Enter fullscreen mode Exit fullscreen mode

βœ… Solution: Use componentWillUnmount() to clean it 🧹

class TimerComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds: 0 };
  }

  componentDidMount() {
    this.interval = setInterval(() => {
      this.setState((prev) => ({ seconds: prev.seconds + 1 }));
    }, 1000);
  }

  // 🧹 Cleanup before unmount
  componentWillUnmount() {
    clearInterval(this.interval); // βœ… Stop the timer
    console.log("🧹 TimerComponent unmounted, interval cleared");
  }

  render() {
    return <h2>⏱️ Timer: {this.state.seconds}s</h2>;
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ” Switch Component Example to Show Cleanup in Action

class Parent extends React.Component {
  constructor() {
    super();
    this.state = { showTimer: true };
  }

  toggle = () => {
    this.setState({ showTimer: !this.state.showTimer });
  };

  render() {
    return (
      <div>
        <button onClick={this.toggle}>
          {this.state.showTimer ? "❌ Stop Timer" : "▢️ Start Timer"}
        </button>
        {this.state.showTimer && <TimerComponent />}
      </div>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ When you press the button, TimerComponent is removed.
βœ… componentWillUnmount() fires β†’ clears interval
βœ… No memory leaks πŸ”₯


πŸ“¦ Other Use Cases – Mini Examples

🎧 Remove Event Listener

componentDidMount() {
  window.addEventListener("resize", this.handleResize);
}

componentWillUnmount() {
  window.removeEventListener("resize", this.handleResize);
}
Enter fullscreen mode Exit fullscreen mode

πŸ“‘ Cancel API Call (with AbortController)

componentDidMount() {
  this.controller = new AbortController();

  fetch("https://api.example.com/data", { signal: this.controller.signal })
    .then(res => res.json())
    .then(data => this.setState({ data }));
}

componentWillUnmount() {
  this.controller.abort(); // ❌ Stop pending fetch
}
Enter fullscreen mode Exit fullscreen mode

πŸ”„ React Hook Equivalent (For Functional Components)

useEffect(() => {
  const interval = setInterval(() => {
    // logic
  }, 1000);

  return () => {
    clearInterval(interval); // 🧹 same cleanup
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

🧠 Summary

Aspect componentWillUnmount()
Runs when? Just before component is removed
Common Use Clearing intervals, listeners, subscriptions
Hook Equivalent useEffect(() => {...; return () => {...}}, [])
Helps prevent Memory leaks, console warnings, invalid updates

🧠 How to Clear setTimeout in useEffect() + Execution Flow Explained


βœ… Code Snippet You Asked:

useEffect(() => {
  console.log("πŸ”₯ useEffect runs");

  return () => {
    console.log("🧹 Cleanup runs (if any)");
  };
}, []);

console.log("πŸ–¨οΈ Render runs");
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ πŸ”„ Output Order:

πŸ–¨οΈ Render runs
πŸ”₯ useEffect runs
Enter fullscreen mode Exit fullscreen mode

Because:

  • console.log("render") happens on each render
  • useEffect() runs after render (πŸ“¦ commit phase)
  • Cleanup function runs before next effect OR on unmount

πŸ’£ If you use setTimeout without clearing:

useEffect(() => {
  setTimeout(() => {
    console.log("⏰ Timeout executed!");
  }, 5000);
}, []);
Enter fullscreen mode Exit fullscreen mode

πŸ’₯ Problem: If component unmounts before 5s, the callback still runs!
β†’ This might lead to memory leaks or trying to update unmounted components.


βœ… Clear setTimeout like this:

useEffect(() => {
  console.log("πŸ”₯ useEffect with timeout");

  const timeoutId = setTimeout(() => {
    console.log("⏰ Timeout fired!");
  }, 5000);

  // 🧹 Cleanup
  return () => {
    clearTimeout(timeoutId);
    console.log("🧼 Timeout cleared!");
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

πŸ§ͺ Real Example with Toggle

import React, { useState, useEffect } from "react";

const TimeoutComponent = () => {
  const [visible, setVisible] = useState(true);

  return (
    <div>
      <button onClick={() => setVisible(!visible)}>
        {visible ? "❌ Hide" : "βœ… Show"} Component
      </button>

      {visible && <ChildWithTimeout />}
    </div>
  );
};

const ChildWithTimeout = () => {
  useEffect(() => {
    const timeout = setTimeout(() => {
      console.log("⏰ Timeout triggered!");
    }, 5000);

    return () => {
      clearTimeout(timeout);
      console.log("🧹 Timeout cleared!");
    };
  }, []);

  return <h3>πŸ‘‹ Hello! I will timeout in 5s unless unmounted.</h3>;
};

export default TimeoutComponent;
Enter fullscreen mode Exit fullscreen mode

🧠 Summary

πŸ” Concept βœ… React Way
Set a timeout const id = setTimeout(..., time)
Clean it up return () => clearTimeout(id)
Where? Inside useEffect()
Cleanup timing When component unmounts / effect re-runs
Prevents Memory leaks, zombie callbacks

❓ Why Can’t We Write async Directly in useEffect()?

βœ… Short Answer:

You can’t make the useEffect callback itself async because it is expected to return either:

  • undefined (nothing), or
  • a cleanup function

But async functions always return a Promise, which breaks the rules of useEffect.


πŸ§ͺ Let's See What Fails ❌

useEffect(async () => {
  const data = await fetch("https://api.example.com");
  console.log(data);
}, []);
Enter fullscreen mode Exit fullscreen mode

πŸ›‘ Error:

Effect callbacks are synchronous to prevent race conditions.
You wrote an async function that returns a Promise instead of a cleanup function.


🚫 Why React Says NO to async useEffect()

  • useEffect() expects:

    • a sync function
    • that optionally returns a cleanup function

πŸ” But async always returns a Promise, like:

async function x() {
  return "hello";
}
// x() returns Promise<"hello">
Enter fullscreen mode Exit fullscreen mode

So when you do:

useEffect(async () => {
  // ...
}, []);
Enter fullscreen mode Exit fullscreen mode

You’re giving React something like:

useEffect(() => Promise<...>) // ❌ Invalid
Enter fullscreen mode Exit fullscreen mode

Which React does not know how to handle!


βœ… The Correct Pattern: Define Async Inside

useEffect(() => {
  const fetchData = async () => {
    try {
      const res = await fetch("https://api.github.com/users/dpvasani");
      const data = await res.json();
      console.log(data);
    } catch (err) {
      console.error("❌ Fetch error:", err);
    }
  };

  fetchData(); // βœ… Call async inside sync function
}, []);
Enter fullscreen mode Exit fullscreen mode

🧠 Analogy: "πŸš• Uber Driver"

  • useEffect() is like a driver who:

    • πŸš— Picks you up
    • 🚿 Cleans the seat afterward (cleanup function)
  • But if you give them an async trip that doesn’t finish immediately (a Promise)...

    • ❌ They don’t know when to clean the seat
    • ❌ They can't handle unhandled promises

✨ Bonus: What if You NEED await in Cleanup?

If you're doing something async during cleanup, you must wrap it safely:

useEffect(() => {
  const fetchSomething = async () => { /* await here */ };
  fetchSomething();

  return () => {
    // 🚫 avoid: directly writing async
    (async () => {
      await doSomethingAsync(); // βœ… safe pattern
    })();
  };
}, []);
Enter fullscreen mode Exit fullscreen mode

🧠 TL;DR Summary

❓ Question βœ… Answer
Can we use async directly? ❌ No
Why not? It returns a Promise instead of cleanup
How to fix it? Create & call an async function inside the effect
Can we use await in cleanup? βœ… Yes, but use an IIFE: (async () => {})()

πŸ’‘ React Class-Based Component – Multiple state Variables

πŸ‘¨β€πŸ« Equivalent to multiple useState() in functional components, but done with style in class components!


πŸš€ Full Example: Class Component with Multiple States

import React from "react";

class MultiStateExample extends React.Component {
  constructor(props) {
    super(props);

    // 🧠 Initialize multiple state variables
    this.state = {
      count: 0,            // πŸ”’ Number state
      name: "Darshan",     // πŸ‘€ String state
      isLoggedIn: false,   // πŸ” Boolean state
    };
  }

  render() {
    return (
      <div>
        <h2>πŸ‘€ Name: {this.state.name}</h2>
        <h3>πŸ”’ Count: {this.state.count}</h3>
        <h4>πŸ” Logged In: {this.state.isLoggedIn ? "βœ… Yes" : "❌ No"}</h4>

        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          βž• Increment Count
        </button>

        <button onClick={() => this.setState({ isLoggedIn: !this.state.isLoggedIn })}>
          πŸ”„ Toggle Login
        </button>
      </div>
    );
  }
}

export default MultiStateExample;
Enter fullscreen mode Exit fullscreen mode

πŸ“Œ Key Concepts Recap

🧠 Concept βœ… Code Example πŸ’¬ Explanation
Single State Object this.state = { count: 0, name: "..." } All states live inside one object
Update One Field Only this.setState({ count: newCount }) React auto-merges this field into the state
No Need to Spread Manually No need for { ...prevState } like in useState() Class state is smart, no spread required! πŸ€“

πŸ” Functional Component Equivalent

const [count, setCount] = useState(0);
const [name, setName] = useState("Darshan");
const [isLoggedIn, setIsLoggedIn] = useState(false);
Enter fullscreen mode Exit fullscreen mode

πŸ†š In function components, each useState() call manages one value individually.
In class components, everything lives inside this.state 🏠.


🎯 Quick Reminders

  • βœ… setState() only updates the specific property, no full overwrite needed.
  • 🧽 Ideal for grouped values that logically belong together (like form data).
  • πŸ”₯ Cleaner than using multiple useState() in simple scenarios.

Top comments (0)