DAY 25: Understanding Class Components, Lifecycle Methods, Custom useState Hook & Virtual DOM | My Web Dev Journey – ReactJS


🚀 Introduction
Welcome to Day 25 of my Web Development Journey!
Building on the fundamentals of HTML, CSS, and JavaScript, I’ve been diving deep into ReactJS — one of the most popular libraries for building dynamic user interfaces.
Recently, I’ve explored key concepts such as:
Class Components, Lifecycle Methods, Higher Order Components, and built a useState Hook from Scratch.
I’ve also gained insight into React’s Virtual DOM, Reconciliation, and Diffing Algorithm — all crucial for optimizing app performance.
I’m documenting this journey publicly — both to stay consistent and to help others learning React from scratch.
📅 Here’s What I Learned Over the Last 3 Days:
Day 22:
- Class Components
- Lifecycle Methods
- Higher Order Components
Day 23:
- Created useState Hook from Scratch
Day 24:
- Virtual DOM
- Reconciliation
- Diffing Algorithm
Let’s break down each of these topics below 👇
1. Class Components in React:
Before functional components and hooks became the norm, React applications were primarily built using class components.
Class components are ES6 classes that extend the base React.Component
class and must include a render()
method that returns JSX.
Syntax of a Class Component:
Here’s a simple class component called Welcome
that returns a greeting message:
import React from "react";
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
export default Welcome;
Explanation:
Welcome
is a React class component.- It extends from
React.Component
to inherit React’s built-in functionality. - It contains a
render()
method that returns JSX. props
can be accessed usingthis.props
.
Example: Counter Using Class Component with State
Class components allow us to manage internal component state using this.state
and this.setState()
.
Here’s a simple counter component:
import React from "react";
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
this.setState({ count: this.state.count - 1 });
};
render() {
return (
<div>
<h2>Count: {this.state.count}</h2>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
</div>
);
}
}
export default Counter;
How It Works:
- We initialize the state inside the
constructor
. this.setState()
is used to update the state and re-render the component.- We use arrow functions for event handlers to preserve the
this
context. - State changes trigger re-renders, allowing real-time updates to the UI.
2. Lifecycle Methods:
In React Class Components, lifecycle methods are special methods that are invoked at specific stages of a component's existence — from creation to updates and finally to unmounting.
They allow us to run custom logic during these key phases of a component's life.
The 3 Major Phases of a Component's Lifecycle:
- Mounting (Component is being created and inserted into the DOM)
- Updating (Component is being re-rendered due to changes in props or state)
- Unmounting (Component is being removed from the DOM)
1. Mounting Phase:
When a component is created and added to the DOM.
constructor(props)
- Initializes state and binds methods.
- Called once at the beginning
constructor(props) { super(props); this.state = { count: 0 }; }
render()
- Returns the JSX to render.
- Called during mounting and every update.
- Should remain pure and side-effect-free
render() { return ( <div> <h2>Count: {this.state.count}</h2> <button onClick={this.increment}>Increment</button> </div> ); }
componentDidMount()
- Invoked once after the component is
mounted
. - Perfect for
API calls
,timers
,event listeners
.componentDidMount() { console.log("Component mounted"); }
2. Updating Phase:
Triggered by state/props change, causing a re-render.
render()
- Called again to update the DOM with new state/props.
componentDidUpdate(prevProps, prevState)
- Runs after the component updates.
- Compare
prevProps
orprevState
with current ones.componentDidUpdate(prevProps, prevState) { if (prevState.count !== this.state.count) { console.log("Count updated to", this.state.count); } }
3. Unmounting Phase:
Occurs right before the component is removed from the DOM.
componentWillUnmount()
- Used for cleanup (clearing intervals, removing listeners, aborting fetch requests, etc.)
componentWillUnmount() { console.log("Component will unmount"); clearInterval(this.timer); }
Example:
Here’s a full example of a class-based React component that includes the key lifecycle methods:
import React from "react";
class Example extends React.Component {
componentDidMount() {
console.log("Component mounted");
}
componentDidUpdate() {
console.log("Component updated");
}
componentWillUnmount() {
console.log("Component will unmount");
}
render() {
return (
<div>
<h2>Example Component</h2>
<p>This is a simple class component demonstrating lifecycle methods.</p>
</div>
);
}
}
export default Example;
Explanation:
componentDidMount()
logs a message when the component is first added to the DOM.componentDidUpdate()
logs a message whenever the component is updated.componentWillUnmount()
logs a message just before the component is removed.render()
returns JSX with a simple div.
3. Higher Order Components (HOC):
A Higher Order Component (HOC) is a function that takes a component and returns a new component with enhanced behavior.
It’s a pattern used to reuse component logic across multiple components.
Syntax:
const EnhancedComponent = (OriginalComponent) => {
class NewComponent extends React.Component {
// add shared logic here
render() {
return <OriginalComponent {...this.props} />;
}
}
return NewComponent;
};
Explanation:
EnhancedComponent = (OriginalComponent) => { ... }
– Defines a HOC function that takes a component and returns an enhanced version.class NewComponent extends React.Component
– Creates a wrapper component to add reusable logic.return <OriginalComponent {...this.props} />;
– Renders the original component with all passed props preserved.return NewComponent;
– Returns the enhanced component from the HOC.
Why Use HOC?
- Reuse logic between components (e.g., counters, authentication, theming).
- Keep components clean and focused on UI.
- Avoid code duplication.
Example: withCounter HOC
We’ll create a simple HOC that adds counter logic to any component.
Step 1: Create the HOC
import React from "react";
const withCounter = (WrappedComponent) => {
return class WithCounter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment = () => {
this.setState((prevState) => ({
count: prevState.count + 1
}));
};
render() {
return (
<WrappedComponent
count={this.state.count}
increment={this.increment}
{...this.props}
/>
);
}
}
};
export default withCounter;
Step 2: Use the HOC
import withCounter from "./withCounter";
const ClickCounter = ({ count, increment }) => {
return <button onClick={increment}>Clicked {count} times</button>;
};
export default withCounter(ClickCounter);
We can also reuse the same withCounter
HOC for another component like HoverCounter
.
Notes:
- HOCs don't modify the original component — they wrap it.
- Always pass props using
{...this.props}
to maintain prop chaining. - Naming convention usually starts with
with
, e.g.,withAuth
,withTheme
,withCounter
. - HOCs are a great way to abstract non-UI logic from presentation components.
4. Creating useState Hook From Scratch:
This simplified custom useState
hook mimics React's built-in useState
behavior using basic JavaScript.
How It Works:
- We maintain a global
states
array to store state values for multiple hooks. stateIndex
tracks the current hook's position during rendering.- When
useState
is called:- It checks if a value already exists at
states[stateIndex]
. If not, it initializes it withinitialValue
. - It saves the current
stateIndex
tolocalIndex
to capture the correct slot. - It defines
setState
to update the stored state atlocalIndex
, resetstateIndex
to 0, and re-render the component. - It increments
stateIndex
for the next hook call. - It returns the current state value and the
setState
function as an array.
- It checks if a value already exists at
Key Points:
- The global
states
array keeps track of all hook states across renders. stateIndex
ensures the correct state value corresponds to the correct hook call order.- Resetting
stateIndex
before re-render is crucial to maintain synchronization between hook calls and state slots. - This example assumes a simple single-component environment where
render()
triggers re-rendering.
Code:
let states = [];
let stateIndex = 0;
export function useState(initialValue) {
states[stateIndex] = states[stateIndex] ?? initialValue;
const localIndex = stateIndex;
console.log(states);
const setState = (newState) => {
states[localIndex] = newState;
stateIndex = 0;
render(<App />, document.getElementById("root"));
};
stateIndex++;
return [states[localIndex], setState];
}
Final Thoughts:
- Building a custom
useState
hook helps me to understand how React manages state internally. - It shows the importance of preserving the order of hooks and synchronizing updates during re-renders.
- This simplified version works for basic cases but doesn’t cover advanced features like batching or multiple components.
- React’s real implementation handles more complex scenarios and ensures hooks are called in a consistent order.
- Understanding these fundamentals deepens mine grasp of React hooks and prepares me for advanced patterns and optimizations.
5. Virtual DOM:
The Virtual DOM (VDOM) is a lightweight JavaScript representation of the actual DOM (Document Object Model).
It is an in-memory tree structure that mimics the real DOM, but without directly touching the browser.
React uses the Virtual DOM to optimize UI updates and make rendering more efficient.
Why Does the Virtual DOM Exist?
Direct DOM manipulation is slow and expensive, especially when frequent updates happen in modern UIs.
To solve this, React introduced the Virtual DOM as an intermediate step:
- React updates the Virtual DOM first.
- It then compares the new Virtual DOM with the previous one (a process called diffing).
- Based on differences, React updates only the parts of the real DOM that actually changed.
This approach minimizes performance costs and improves responsiveness.
Benefits of the Virtual DOM:
Performance Optimization
Avoids unnecessary DOM updates by batching and minimizing re-renders.Efficient UI Updates
React only updates changed nodes, reducing heavy browser operations.Declarative Programming
We write what the UI should look like, React handles how to update it efficiently.Cross-Platform Rendering
Virtual DOM can be adapted to work with other rendering targets (e.g., React Native).Better Developer Experience
Developers don’t need to manually manage the DOM — React does it automatically.
6. Reconciliation:
Reconciliation is the process React uses to update the DOM when your application’s state or props change.
Rather than updating the entire DOM, React performs a diffing algorithm between the previous Virtual DOM and the new Virtual DOM to determine the minimal set of changes needed.
This process ensures the UI stays in sync with the application state efficiently.
How Does Reconciliation Work?
When a component’s state
or props
change, here’s what happens behind the scenes:
Render Phase (Create New Virtual DOM):
React calls the component’srender()
function to produce a new Virtual DOM tree.Diffing Phase (Compare with Previous Virtual DOM):
React compares the new Virtual DOM with the previous one to detect what has changed (nodes, attributes, text, etc.).Commit Phase (Update Real DOM):
React applies only the necessary changes to the actual DOM — this is called DOM patching.
Benefits of Reconciliation:
- Improved performance through efficient DOM updates.
- Smooth user experience with fewer layout thrashes or reflows.
- Predictable rendering with declarative code structure.
- Enables features like React’s Concurrent Mode and time-slicing.
7. Diffing Algorithm:
React's diffing algorithm is a core part of its reconciliation process.
It compares the previous Virtual DOM and the new Virtual DOM to find the minimum number of operations needed to update the real DOM.
Instead of re-rendering the entire UI, React updates only the parts that changed, making it highly performant.
How Does the Diffing Algorithm Work?
React’s diffing follows these rules and assumptions to optimize updates:
1. Comparing Elements of Different Types
If the element types are different (e.g., <div>
vs <span>
, or ComponentA
vs ComponentB
):
- React will tear down the old tree and build a new one.
- This means all child elements are unmounted and recreated.
// From this:
<div>Hello</div>
// To this:
<span>Hello</span>
React destroys the <div>
and creates a new <span>
.
2. Comparing Elements of the Same Type
If the element types are the same:
- React keeps the same DOM node and only updates the
attributes
orprops
.
// From:
<button className="red">Click</button>
// To:
<button className="blue">Click</button>
React updates only the className
instead of replacing the entire button.
3. Text Content Differences
If only the text content changes, React directly updates the text node
:
// From:
<p>Hello</p>
// To:
<p>Goodbye</p>
React replaces the text "Hello"
with "Goodbye"
without touching the <p>
tag.
4. Children Diffing (Lists)
For elements with multiple children (e.g., <ul>
or <div>
with nested items), React uses key
props to:
- Identify which items changed, were added, or removed.
- Prevent unnecessary re-renders and improve performance.
Using stable and unique key
values helps React efficiently update lists without re-rendering the entire subtree.
// Before:
<ul>
<li key="1">Apple</li>
<li key="2">Banana</li>
</ul>
// After:
<ul>
<li key="2">Banana</li>
<li key="1">Apple</li>
</ul>
Using key
props helps React detect reordering instead of deleting and recreating nodes.
Without keys, React may re-render entire subtrees, leading to performance issues or unexpected behavior.
Why Is It Important?
React’s diffing algorithm is what makes the Virtual DOM efficient.
By batching updates and comparing nodes intelligently, React delivers fast, smooth, and predictable UI updates — even in large applications.
Understanding how diffing works helps us:
- Write better performance-optimized components.
- Use key props effectively.
- Avoid unnecessary re-renders.
8. What’s Next:
I’m excited to keep growing and sharing along the way! Here’s what’s coming up:
- Posting new blog updates every 3 days to share what I’m learning and building.
- Diving deeper into Data Structures & Algorithms with Java — check out my ongoing DSA Journey Blog for detailed walkthroughs and solutions.
- Sharing regular progress and insights on X (Twitter) — feel free to follow me there and join the conversation!
Thanks for being part of this journey!
9. Conclusion:
In this blog of my web dev journey, we explored several fundamental React concepts that form the backbone of modern web development:
- Class Components and how they manage state and lifecycle.
- Key Lifecycle Methods like
componentDidMount
,componentDidUpdate
, andcomponentWillUnmount
. - The power of Higher Order Components (HOCs) to reuse component logic.
- Building a simple useState Hook from scratch to understand React’s state management.
- The role and benefits of the Virtual DOM in optimizing UI updates.
- How Reconciliation helps React efficiently update the DOM.
- The Diffing Algorithm that compares and updates elements smartly, especially with lists and keys.
Mastering these concepts provides a strong foundation for building performant and maintainable React applications. Keep practicing and experimenting with these ideas to deepen my understanding and improve my skills as a React developer.
If you're on a similar journey, feel free to follow along or connect — we’re all in this together!
Subscribe to my newsletter
Read articles from Ritik Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ritik Kumar
Ritik Kumar
👨💻 Aspiring Software Developer | MERN Stack Developer.🚀 Documenting my journey in Full-Stack Development & DSA with Java.📘 Focused on writing clean code, building real-world projects, and continuous learning.