π 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
, sosuper()
refers toReact.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
}
β
With super(props)
:
constructor(props) {
super(props); // β
console.log(this.props); // β
accessible
}
π’ 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;
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;
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;
π‘ 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>
);
};
π© 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>
);
}
}
π 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])
andcomponentDidUpdate()
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]);
is replicated in Class-based components.
β Hook Behavior Recap:
useEffect(() => {
// runs when either count1 or count2 changes
}, [count1, count2]);
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;
π§ 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 π±
β
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>;
}
}
π 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>
);
}
}
π 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);
}
π‘ 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
}
π React Hook Equivalent (For Functional Components)
useEffect(() => {
const interval = setInterval(() => {
// logic
}, 1000);
return () => {
clearInterval(interval); // π§Ή same cleanup
};
}, []);
π§ 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");
π π Output Order:
π¨οΈ Render runs
π₯ useEffect runs
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);
}, []);
π₯ 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!");
};
}, []);
π§ͺ 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;
π§ 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 ofuseEffect
.
π§ͺ Let's See What Fails β
useEffect(async () => {
const data = await fetch("https://api.example.com");
console.log(data);
}, []);
π Error:
Effect callbacks are synchronous to prevent race conditions.
You wrote anasync
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">
So when you do:
useEffect(async () => {
// ...
}, []);
Youβre giving React something like:
useEffect(() => Promise<...>) // β Invalid
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
}, []);
π§ 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 (aPromise
)...- β 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
})();
};
}, []);
π§ 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;
π 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);
π 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)