0% found this document useful (0 votes)
17 views

Code Evolution React Hook

The document provides a comprehensive overview of React Hooks, introduced in version 16.8, which allow developers to manage state and lifecycle methods in functional components without using class components. It discusses the motivation behind Hooks, their advantages, and provides code examples to illustrate the transition from class-based to functional components with Hooks. Key takeaways include the optional nature of Hooks, their backward compatibility, and the simplification of state management and code organization.

Uploaded by

shreyas
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views

Code Evolution React Hook

The document provides a comprehensive overview of React Hooks, introduced in version 16.8, which allow developers to manage state and lifecycle methods in functional components without using class components. It discusses the motivation behind Hooks, their advantages, and provides code examples to illustrate the transition from class-based to functional components with Hooks. Key takeaways include the optional nature of Hooks, their backward compatibility, and the simplification of state management and code organization.

Uploaded by

shreyas
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 164

Code evolution React hook

Here are the detailed notes with improvements, added code snippets to
demonstrate the problems and how they were solved using React Hooks:

📹 React Hooks - Introduction and Motivation


🎤 Host: Vishwas
📅 React Version: 16.8 or higher
💡 Focus: Introduction to React Hooks
🔹 What are Hooks in React?
📌 Definition:
Hooks are a feature added in React 16.8 that lets you use React features like
state and lifecycle methods without needing to write a class component.

🛑 Key Point:
Hooks don’t work inside classes, and they allow you to use React features
like state, context, and others in functional components.

⚙️ Example:
Before Hooks: Only class components could hold state.

After Hooks: With useState, you can manage state in functional


components.

🔹 Why Hooks?
❓ Motivation:
Why did React team introduce Hooks? Here's the why behind it:

1️⃣ Dealing with JavaScript Classes


🚨 Issues with Classes:
Understanding the this keyword in JavaScript was difficult for many
developers.

Code evolution React hook 1


Binding event handlers was cumbersome and error-prone.

Code Example (Before Hooks):

// Before: Binding 'this' in a class component


class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.increment = this.increment.bind(this);
}

increment() {
this.setState({ count: this.state.count + 1 });
}

render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</butt
on>
</div>
);
}
}

Problems: Manual this binding in constructors, and this confusion.

🔧 Solution with Hooks:


No need to use classes or worry about this keyword.

useState allows you to manage state in functional components without


this .

Code Example (After Hooks):

// After: Using useState hook in a functional component


import React, { useState } from 'react';

Code evolution React hook 2


const Counter = () => {
const [count, setCount] = useState(0);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increm
ent</button>
</div>
);
};

export default Counter;

Advantages: No binding required, cleaner and easier to understand.

2️⃣ Reusing Stateful Logic


💭 Problem:
In React, there was no standard way to reuse stateful logic across
components. Developers had to use patterns like Higher-Order
Components (HOCs) or Render Props, which could make the code harder
to follow and resulted in prop-drilling.

Code Example (Before Hooks):

// Before: Using HOC to reuse logic


function withCounter(WrappedComponent) {
return function EnhancedComponent(props) {
const [count, setCount] = useState(0);

return <WrappedComponent count={count} setCount={set


Count} {...props} />;
};
}

Problems: Adding unnecessary complexity with HOCs.

🔧 Solution with Hooks:

Code evolution React hook 3


With useState and useEffect, you can reuse stateful logic directly without
the need for HOCs or render props.

Code Example (After Hooks):

// After: Using custom hooks for reusable logic


function useCounter() {
const [count, setCount] = useState(0);
return { count, increment: () => setCount(count + 1)
};
}

const Counter = () => {


const { count, increment } = useCounter();

return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};

export default Counter;

Advantages: Reusable, cleaner, and more understandable logic with


custom hooks.

3️⃣ Organizing Complex Components


🧩 Problem:
In class components, related code (such as data fetching and event
listeners) was scattered across multiple lifecycle methods. This made the
code harder to maintain and debug.

Code Example (Before Hooks):

// Before: Using component lifecycle methods for data fe


tching
class DataFetcher extends React.Component {

Code evolution React hook 4


state = { data: null, loading: true };

componentDidMount() {
fetchData().then((data) => {
this.setState({ data, loading: false });
});
}

componentWillUnmount() {
cleanupEventListener();
}

render() {
const { data, loading } = this.state;
return loading ? <p>Loading...</p> : <p>{JSON.string
ify(data)}</p>;
}
}

Problems: Related logic is scattered between componentDidMount ,


componentWillUnmount .

🔧 Solution with Hooks:


With useEffect, related logic can be grouped together, making the code
cleaner and easier to understand.
Code Example (After Hooks):

// After: Using useEffect for data fetching and cleanup


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

const DataFetcher = () => {


const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetchData().then((data) => {
setData(data);
setLoading(false);

Code evolution React hook 5


});

return () => cleanupEventListener(); // Cleanup log


ic in the return function
}, []); // Empty dependency array to ensure this runs
once

return loading ? <p>Loading...</p> : <p>{JSON.stringif


y(data)}</p>;
};

export default DataFetcher;

Advantages: All related logic (fetching and cleanup) is in one place


using useEffect .

Here’s a detailed and organized note based on your provided points:

🔹 React Hooks - Key Facts


📅 React Version: Hooks are available from React 16.8 and onwards.
👌 Completely Opt-In:
Hooks are entirely optional; you can choose to use them or stick with
classes.

They are designed to coexist with existing React features like class
components.

🔄 Backward Compatibility:
Hooks don’t introduce breaking changes.

The release is 100% backwards-compatible, ensuring no disruption to


existing projects.

🛑 Classes Are Here to Stay:


React won’t remove classes.

You can gradually adopt hooks without completely rewriting your app.

Code evolution React hook 6


🚫 Limitations of Hooks:
Hooks cannot be used inside class components. They are exclusive to
functional components.

🎯 Hooks’ Purpose:
Hooks don’t replace your existing knowledge of React concepts like
props, state, context, refs, or lifecycle methods.

Instead, they provide a more direct and intuitive API for working with
these concepts in functional components.

⚙️ Example Code for Context


Class Component (Pre-Hooks):

class Counter extends React.Component {


constructor(props) {
super(props);
this.state = { count: 0 };
}

increment = () => {
this.setState({ count: this.state.count + 1 });
};

render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}

Functional Component with Hooks:

import React, { useState } from 'react';

Code evolution React hook 7


function Counter() {
const [count, setCount] = useState(0);

return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment
</button>
</div>
);
}

Advantages of Using Hooks in the Above Example:

No need for this keyword or constructor .

State logic is cleaner and more concise.

Easier to reuse stateful logic if needed.

Let me know if you'd like more examples or deeper explanations! 😊


📚 Purpose of Hooks:
Hooks don't replace your existing knowledge of React. Instead, they
provide a simpler, more direct API for concepts like state, props, context,
and lifecycle methods.

🔹 Summary
📌 Key Takeaways:
1. Hooks: A new feature in React 16.8 that allows you to use React
features (like state) without classes.

2. Benefits:

Avoid confusion with this keyword.

Easier to reuse stateful logic.

Better organization of related code.

3. Opt-In: You can continue using classes if preferred. Hooks are optional
and backward compatible.

Code evolution React hook 8


Transcript Notes: React State Hook (useState) Introduction 🧵
🌟 Overview
React hooks allow state and other React features in functional components.

The useState hook introduces state in functional components, replacing


the need for class components when state is required.

🛠️ Step-by-Step Guide:
1️⃣ Class Component Implementation
Steps to build a counter with class components:

1. Create a component (class-based).

2. Define a state variable (e.g., count , initialized to 0 ).

3. Define a method to update the state (e.g., incrementCount using


setState ).

Code Example:

import React, { Component } from 'react';

class ClassCounter extends Component {


state = { count: 0 };

incrementCount = () => {
this.setState({ count: this.state.count + 1 });
};

render() {
return (
<button onClick={this.incrementCount}>
Count: {this.state.count}
</button>
);
}
}

Code evolution React hook 9


export default ClassCounter;

Usage:
Include
<ClassCounter /> in your App.js and test in the browser.

2️⃣ Functional Component with useState Hook


Steps to build the counter:

1. Create a functional component.

2. Use the useState hook for state management:

Import useState from React.

Call useState(initialValue) to create a state variable and its updater


function.

3. Update the state using the updater function.

Code Example:

import React, { useState } from 'react';

const HookCounter = () => {


const [count, setCount] = useState(0); // State vari
able and updater

return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
};

export default HookCounter;

Explanation:

useState(0) initializes count to 0 .

Code evolution React hook 10


setCount updates the state, triggering a re-render.

🔑 Key Points About useState

1. Import and Usage:

Import useState from React.

Call useState with the initial state value.

Destructure the return values: [currentState, setStateFunction] .

2. Why useState ?

Simplifies state usage in functional components.

No need for this , making the code cleaner.

3. Array Destructuring:

Example:

const [count, setCount] = useState(0);

Destructuring assigns:

count : Current state value.

setCount : Function to update the state.

📜 Rules of Hooks:
1. Call Hooks at the Top Level:

Do not use inside loops, conditions, or nested functions.

2. Only Call Hooks from React Functions:

Use inside functional components or custom hooks, not regular


JavaScript functions.

📝 Recap:
Class components are no longer necessary for state management with the
advent of hooks.

The useState hook is:

Code evolution React hook 11


Easy to implement.

Cleaner and more readable compared to class-based state


management.

Try replacing class components in your projects with functional components


and hooks to modernize your React code! 🎉
Transcript Notes: Using Objects as State Variables in React 🧵
🌟 Overview:
This video discusses:

1. Using objects as state variables with the useState hook.

2. Why useState does not automatically merge object properties (unlike


setState in class components).

3. How to use the spread operator to manually merge state updates.

🛠️ Step-by-Step Guide:
1️⃣ Initial Setup:
Create the Component:
File:
HookCounterThree.js

import React, { useState } from 'react';

const HookCounterThree = () => {


const [name, setName] = useState({ firstName: '', la
stName: '' });

return (
<div>
<form>
<input
type="text"
value={name.firstName}
onChange={(e) =>
setName({ ...name, firstName: e.

Code evolution React hook 12


target.value })
}
/>
<input
type="text"
value={name.lastName}
onChange={(e) =>
setName({ ...name, lastName: e.t
arget.value })
}
/>
<h2>Your first name is: {name.firstName}
</h2>
<h2>Your last name is: {name.lastName}</
h2>
<p>{JSON.stringify(name)}</p>
</form>
</div>
);
};

export default HookCounterThree;

import React, { useState } from 'react';


import { View, Text, TextInput, StyleSheet } from 'react-nativ

const HookCounterThree = () => {


const [name, setName] = useState({ firstName: '', lastName

return (
<View style={styles.container}>
<TextInput
style={styles.input}
placeholder="First Name"
value={name.firstName}
onChangeText={(value) =>

Code evolution React hook 13


setName({ ...name, firstName: value })
}
/>
<TextInput
style={styles.input}
placeholder="Last Name"
value={name.lastName}
onChangeText={(value) =>
setName({ ...name, lastName: value })
}
/>
<Text style={styles.text}>Your first name is: {nam
<Text style={styles.text}>Your last name is: {name
<Text style={styles.text}>State: {JSON.stringify(
</View>
);
};

const styles = StyleSheet.create({


container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
input: {
height: 40,
borderColor: '#ccc',
borderWidth: 1,
marginBottom: 10,
paddingHorizontal: 8,
width: '100%',
borderRadius: 5,
},
text: {
fontSize: 16,
marginTop: 10,
},

Code evolution React hook 14


});

export default HookCounterThree;

The spread syntax ( ... ) in JavaScript allows iterable elements like arrays or
strings to be expanded in places where zero or more arguments or elements
are expected.

MDN Web Docs


Internally, when you use the spread syntax, JavaScript utilizes the object's
iterator to retrieve its elements. This means that the spread syntax works with
any object that implements the iterable protocol, such as arrays, strings, sets,
and maps.
Key Points:

Iterables Expansion: The spread syntax expands iterable objects into


individual elements. For example, spreading an array [1, 2, 3] in a new
array [...arr] results in [1, 2, 3] .
MDN Web Docs

Function Arguments: When used in function calls, the spread syntax allows
elements of an array to be passed as individual arguments. For instance,
Math.max(...[1, 2, 3]) is equivalent to Math.max(1, 2, 3) .

MDN Web Docs

Object Properties: In object literals, the spread syntax copies properties


from one object to another. For example, { ...obj, newProp: value } creates a
new object with all properties of obj and adds newProp .
MDN Web Docs

It's important to note that the spread syntax performs a shallow copy of the
elements or properties. This means that if the original object contains
references to other objects, only the references are copied, not the actual
objects. Therefore, changes to nested objects will affect both the original and
the copied object.
Kinsta®
In summary, the spread syntax provides a concise and readable way to expand
elements, pass arguments, or merge objects by leveraging the iterable protocol
and object property enumeration in JavaScript.

Code evolution React hook 15


2️⃣ Key Concepts:
1. State Initialization:

initializes the name state variable as an object with


useState firstName

and lastName properties:

const [name, setName] = useState({ firstName: '', las


tName: '' });

2. Two-Way Binding:

value is bound to the respective property in the state.

onChange updates the property in the state.

3️⃣ The Problem:


Without the spread operator:

<input
type="text"
value={name.firstName}
onChange={(e) => setName({ firstName: e.target.value
})}
/>

When the first name is updated, the lastName field disappears from the
state. Why?

Reason: useState does not merge the existing state. It replaces it with
the new object.

4️⃣ The Solution:


Use the spread operator to merge the existing state with the new updates:

setName({ ...name, firstName: e.target.value });

Explanation:

Code evolution React hook 16


...name copies all existing properties from name .

Then, firstName: e.target.value updates the firstName property.

5️⃣ Updated Component:


Here’s the updated component:

import React, { useState } from 'react';

const HookCounterThree = () => {


const [name, setName] = useState({ firstName: '', lastN
ame: '' });

return (
<div>
<form>
<input
type="text"
value={name.firstName}
onChange={(e) =>
setName({ ...name, firstName: e.tar
get.value })
}
/>
<input
type="text"
value={name.lastName}
onChange={(e) =>
setName({ ...name, lastName: e.targ
et.value })
}
/>
<h2>Your first name is: {name.firstName}</h
2>
<h2>Your last name is: {name.lastName}</h2>
<p>{JSON.stringify(name)}</p>
</form>
</div>

Code evolution React hook 17


);
};

export default HookCounterThree;

🔑 Key Points:
1. useState Behavior:

Unlike setState in class components, useState does not merge updates


into the existing state. It replaces the entire state object.

2. Solution:

Use the spread operator to manually merge the existing state with the
updated properties:

setName({ ...name, firstName: 'Updated Name' });

3. Advantages of Functional Components:

Explicit control over state updates.

Better performance due to precise updates.

📝 Recap:
Use objects in state for complex state management.

Always merge object updates manually with the spread operator when
using useState .

This ensures no properties are accidentally removed during updates.

Stay tuned for the next example, where we'll explore working with arrays in
state! 🎉

📝 Transcript Notes: Understanding useState with Arrays in


React

Code evolution React hook 18


Intro
In this video, we explore how to use the useState hook when the state
variable is an array.

A simple example demonstrates how to manage arrays in React state using


the useState hook.

Steps Covered in the Example


1. File Setup:

A new file is created, HookCounterFour.js .

Use the RFC snippet to generate a functional component structure.

2. Declare State with useState :

Import useState from React.

Declare state as an array:

const [items, setItems] = useState([]);

3. Rendering Items:

Use a <ul> tag to render the list of items:

items.map((item) => (
<li key={item.id}>{item.value}</li>
));

Each item is an object with id and value properties.

4. Add Button for New Items:

Add a button labeled "Add a Number":

<button onClick={addItem}>Add a Number</button>

This triggers the addItem function to add new items to the array.

5. addItem Function:

Define the addItem function to append a new object to the items array:

Code evolution React hook 19


const addItem = () => {
setItems([
...items,
{ id: items.length, value: Math.floor(Math.ra
ndom() * 10) + 1 }
]);
};

Key points:

Spread operator ( ...items ) is used to copy the existing array.

A new object is appended with:

id : The current length of the array.

value : A random number between 1 and 10.

How the Spread Operator Works in This Context


The spread operator ( ...items ):

Creates a shallow copy of the items array.

Ensures the original array remains unmodified.

Appending a new object to the copied array avoids directly mutating the
state.

Execution Flow
1. Initially, items is an empty array ( [] ).

2. On clicking the button:

A new object is added to the array.

Example:

{ id: 0, value: 5 }

3. On the second button click:

The array now contains the first item.

Code evolution React hook 20


The second object is appended:

{ id: 1, value: 7 }

4. This process continues, appending new objects for every button click.

Summary
The useState hook:

Adds state to functional components.

Returns an array:

The first element is the current state.

The second element is the setter function.

Key points for arrays and objects:

Always spread the state before updating.

This ensures React properly detects changes and triggers re-renders.

Example usage:

setItems([...items, newItem]);

🚀 Key Takeaways
Use the spread operator for state immutability.

The useState hook simplifies state management for functional components.

For dependent state updates, consider passing a callback to the setter


function:

setItems((prevItems) => [...prevItems, newItem]);

Keep these concepts in mind when working with arrays in React! 🎉


📝 Transcript Notes: Introduction to useEffect Hook in React

Code evolution React hook 21


Introduction
In this session, we dive into the useEffect hook, which helps manage side
effects in React functional components.

Common examples of side effects:

Updating the DOM (e.g., document title).

Fetching data from an API.

Setting up subscriptions or timers.

Motivation for useEffect


In class components, side effects are handled via lifecycle methods like:

1. componentDidMount – Runs once after the component is mounted.

2. componentDidUpdate – Runs after every render update.

3. componentWillUnmount – Runs when the component is about to be unmounted.

Example 1: Updating Document Title

Class Component Approach


We want to update the document title based on a counter value:

1. Initial Render:

In componentDidMount , set the title to "Clicked 0 times" .

componentDidMount() {
document.title = `Clicked ${this.state.count} times
`;
}

2. On State Update:

In componentDidUpdate , update the title when count changes.

componentDidUpdate() {
document.title = `Clicked ${this.state.count} times

Code evolution React hook 22


`;
}

3. Problem:

Code duplication! The same logic is written in two different lifecycle


methods.

Example 2: Timer Setup

Class Component Approach


We want to set up a timer that logs "Hello" every 5 seconds:

1. Set Timer:

In componentDidMount , use setInterval to log "Hello" :

componentDidMount() {
this.interval = setInterval(() => {
console.log('Hello');
}, 5000);
}

2. Clean Up Timer:

In componentWillUnmount , clear the timer:

componentWillUnmount() {
clearInterval(this.interval);
}

3. Problem:

Related logic (e.g., setInterval and clearInterval ) is split across multiple


lifecycle methods.

Observations and Problems


1. Code Duplication:

Code evolution React hook 23


Updating the document title involves repeating code in
componentDidMount and componentDidUpdate .

2. Unrelated Code Grouping:

Unrelated logic (e.g., DOM updates and timers) is often placed together
in componentDidMount .

3. Fragmented Related Logic:

Related logic (e.g., setInterval and clearInterval ) is split across


different methods, making the code harder to maintain.

The Solution: useEffect Hook


useEffect addresses the issues above by:

Combining lifecycle methods into one hook.

Grouping related logic together in a single place.

Eliminating code duplication.

How useEffect Works


1. It is a close replacement for:

componentDidMount

componentDidUpdate

componentWillUnmount

2. Advantages:

Avoids code repetition.

Simplifies grouping and managing side effects in functional


components.

Next Steps
In the next example, we will:

Use useEffect to update the document title on:

Initial render.

Subsequent renders.

Code evolution React hook 24


Manage side effects efficiently with this hook.

🚀 Key Takeaways
useEffect is a powerful hook that simplifies managing side effects in
functional components.

It consolidates three lifecycle methods ( componentDidMount , componentDidUpdate ,


and componentWillUnmount ) into a single hook.

Use useEffect to:

Perform DOM updates.

Fetch data.

Set up and clean up subscriptions or timers.

By grouping related logic, useEffect improves code readability and


maintainability.

Stay tuned for the implementation examples! 🎉

📝 Detailed Notes: Using useEffect for Side Effects in


Functional Components

📹 Introduction:
In the previous video: We explored the useEffect hook and how it allows us
to perform side effects in functional components. It mimics lifecycle
methods like componentDidMount , componentDidUpdate , and componentWillUnmount

from class components.

Goal for this video: Implement side effects such as updating the document
title using the useEffect hook in functional components.

🔧Components):
Class Component Example (Before Functional

class Counter extends React.Component {


constructor() {

Code evolution React hook 25


super();
this.state = { count: 0 };
}

// ComponentDidMount
componentDidMount() {
document.title = `You clicked ${this.state.count} times
`;
}

// ComponentDidUpdate
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times
`;
}

render() {
return (
<button onClick={() => this.setState({ count: this.st
ate.count + 1 })}>
Clicked {this.state.count} times
</button>
);
}
}

Key points:

componentDidMount : Sets the document title after the initial render.

componentDidUpdate : Updates the document title after each re-render (on


button click).

⚙️ Converting to a Functional Component with useEffect :

1. Creating a Counter Component using useState :

import React, { useState } from 'react';

Code evolution React hook 26


const Counter = () => {
const [count, setCount] = useState(0);

return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
};

Key points:

We use useState to maintain the count state.

The button increments the count when clicked.

2. Adding the Side Effect (Document Title Update) using


useEffect :

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

const Counter = () => {


const [count, setCount] = useState(0);

// Side Effect: Update document title after every render


useEffect(() => {
document.title = `You clicked ${count} times`;
});

return (
<button onClick={() => setCount(count + 1)}>
Clicked {count} times
</button>
);
};

How useEffect works:

It runs after every render.

Code evolution React hook 27


The function inside useEffect updates the document title with the
current count value.

🔍 How Does useEffect Work?


useEffect runs after every render of the component.

Initial render: Executes the effect.

Subsequent renders: Executes the effect again.

The reason it runs after every render: To mimic the behavior of lifecycle
methods like componentDidMount and componentDidUpdate from class components.

Accessing State and Props:

useEffect runs inside the component body, so you can directly access
state (e.g., count ) and props without extra code.

💡 Key Takeaways:
useEffect is called after every render.

For side effects that need to run after the first and subsequent renders
(like document updates), useEffect is perfect.

Basic Syntax:

useEffect(() => {
// Your side effect code here
});

You can access state and props directly within the useEffect function
without extra setup.

⏭️ What's Next?
We will dive deeper into customizing when the useEffect hook should run
(e.g., running it only once or when certain state changes).

Thank you for watching!

Code evolution React hook 28


📝 Detailed Notes: Conditionally Running Effects with
useEffect

📹 Introduction:
In the last video: We explored how useEffect is called after every render in
functional components. However, calling useEffect on every render can
cause performance issues.

Goal for this video: Learn how to conditionally run effects to improve
performance by only executing useEffect when certain variables change.

🔧 Class Component Example:


1. Initial Class Component Setup:

class Counter extends React.Component {


constructor() {
super();
this.state = { count: 0, name: '' };
}

// Handle the count update


handleClick = () => {
this.setState({ count: this.state.count + 1 });
};

// Handle name input change


handleInputChange = (e) => {
this.setState({ name: e.target.value });
};

// ComponentDidUpdate
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} tim
es`;
console.log('Updating document title');
}

Code evolution React hook 29


}

render() {
return (
<>
<button onClick={this.handleClick}>Clicked {this.st
ate.count} times</button>
<input
type="text"
value={this.state.name}
onChange={this.handleInputChange}
/>
</>
);
}
}

Key Points:

componentDidUpdate : Updates the document title when the count state


changes, but not when typing in the name.

Optimization: We check if the count has actually changed before


updating the document title to avoid unnecessary re-renders.

⚙️ Adding useEffect to Functional Component:

2. Basic Functional Component Setup:

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

const Counter = () => {


const [count, setCount] = useState(0);
const [name, setName] = useState('');

// Update document title (side effect)


useEffect(() => {
document.title = `You clicked ${count} times`;
console.log('Updating document title');

Code evolution React hook 30


});

return (
<>
<button onClick={() => setCount(count + 1)}>Clicked
{count} times</button>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</>
);
};

Key Points:

Effect Hook: The useEffect runs after every render, updating the
document title even when typing in the input, which is not optimal.

🔍 Conditional Running of Effects with useEffect :

3. Optimizing useEffect by Watching Specific State (e.g.,


count ):

We want the effect to only run when the count value changes and not when
the name is updated. This can be achieved by passing a dependency array
as the second argument to useEffect .

useEffect(() => {
document.title = `You clicked ${count} times`;
console.log('Updating document title');
}, [count]); // Only run effect when `count` changes

Key Points:

Dependency Array: The second argument of useEffect is an array.


React will only re-run the effect if any of the values in this array change.
In this case, we want to track count .

Code evolution React hook 31


Performance Optimization: The effect is now only triggered when
count changes, and not when the name input changes.

💡 Key Takeaways:
with Dependency Array: To conditionally run an effect, pass a
useEffect

second argument (an array) with the state or props you want to watch. The
effect will only run if one of those values changes.

Example:

useEffect(() => {
// Your side effect code here
}, [someStateOrProp]); // Effect runs only if `someSt
ateOrProp` changes

Performance Optimization: By using the dependency array, we avoid


unnecessary re-renders and side effects, improving app performance.

🔧 Summary of Changes Made:


Without Dependency Array: useEffect runs after every render, including
every time the name input changes.

With Dependency Array: We only run useEffect when the count value
changes, optimizing performance.

⏭️ What's Next?
In the next video, we'll explore more examples and dive deeper into the
behavior of useEffect and the dependency array.

Thank you for watching!

🎬 Video Transcript Summary: Mimicking componentDidMount


with useEffect
In this video, we focus on how to run an effect only once, similar to how
componentDidMount works in class components, but using the useEffect hook in

functional components. Here’s a detailed breakdown of the process.

Code evolution React hook 32


💻 Class Component Example:
The initial code uses a class component to track the mouse position on the
screen:

1. State Setup:

Variables to store X and Y coordinates for the mouse pointer.

constructor() {
this.state = { x: 0, y: 0 };
}

2. componentDidMount Hook:

Adds an event listener for mouse move events.

Logs the mouse position whenever the event occurs.

componentDidMount() {
window.addEventListener('mousemove', this.logMousePosi
tion);
}

logMousePosition = (e) => {


this.setState({
x: e.clientX,
y: e.clientY,
});
}

3. Rendering:

The mouse coordinates are rendered in the UI.

This ensures that the event listener only sets up once, on initial mount,
mimicking componentDidMount behavior.

import React, { Component } from 'react';

class MouseTracker extends Component {


constructor(props) {

Code evolution React hook 33


super(props);
this.state = {
x: 0,
y: 0,
};
}

// Component Did Mount - equivalent to `useEffect` with emp


componentDidMount() {
console.log('componentDidMount called');

// Mouse move event listener


this.logMousePosition = (e) => {
this.setState({ x: e.clientX, y: e.clientY });
};

// Adding event listener


window.addEventListener('mousemove', this.logMousePositio
}

// Component Will Unmount - cleanup function to remove even


componentWillUnmount() {
// Removing event listener to avoid memory leaks
window.removeEventListener('mousemove', this.logMousePosi
}

render() {
const { x, y } = this.state;
return (
<div>
X: {x}, Y: {y}
</div>
);
}
}

export default MouseTracker;

Code evolution React hook 34


🧑‍💻 Functional Component with useEffect :
Now, let's rewrite the same functionality using React Hooks:

1. Create a New File:


We’ll create a new file hookMouse.js .

2. Functional Component Setup:


Create a functional component using the RFC snippet.

Import useState and useEffect hooks from React.

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

const HookMouse = () => {


const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

useEffect(() => {
console.log('useEffect called');

const logMousePosition = (e) => {


setMousePos({ x: e.clientX, y: e.clientY });
};

window.addEventListener('mousemove', logMousePosition);

return () => {
window.removeEventListener('mousemove', logMousePosit
ion);
};
}, []); // Empty dependency array

return (
<div>
X: {mousePos.x}, Y: {mousePos.y}
</div>
);
};

Code evolution React hook 35


3. Explanation of useEffect :
The useEffect hook is used to add an event listener for the mousemove event.

Empty Dependency Array [] : By passing an empty array as the


second argument to useEffect , we are telling React to run this effect
only once when the component is first rendered (just like
componentDidMount ).

This setup ensures that the event listener is added once on initial
render and not on subsequent re-renders.

4. Event Listener Cleanup:


Inside useEffect , we also define a cleanup function:

The cleanup function ( return () => {...} ) removes the event listener
when the component is unmounted. This is important to avoid memory
leaks.

return () => {
window.removeEventListener('mousemove', logMousePositio
n);
};

🧩 Key Points to Remember:


Mimicking componentDidMount : By passing an empty array [] as the second
argument to useEffect , we achieve behavior that is equivalent to
componentDidMountin class components. This means the effect is only
triggered on the initial render.

Event Listener Cleanup: React’s useEffect also provides a cleanup function


to remove event listeners when the component is unmounted, ensuring
good resource management and preventing memory leaks.

🔑 Summary of Code Flow:


1. State Initialization: Track the mouse position using useState .

2. useEffect Hook:

Runs once on the initial render.

Code evolution React hook 36


Adds an event listener for the mousemove event.

Logs the mouse position and updates the state.

3. Cleanup: Removes the event listener on unmount to prevent potential


issues.

🚀 Conclusion:
By using useEffect with an empty dependency array, we mimic the
componentDidMount lifecycle method from class components.

This allows us to run effects once when the component is first mounted,
making it a powerful tool for side effects in functional components.

📚 Detailed Notes on Cleanup Code in useEffect (Component


Unmounting)
In this video, we learn how to handle component unmounting using useEffect
and clean up any subscriptions, event listeners, or other resources that might
lead to memory leaks.

🏗️ Objective:
Mimic componentWillUnmount in class components using the useEffect hook in
functional components.

Handle cleanup code (like removing event listeners) when a component is


unmounted.

🔍 Class Component Overview:


In class components, we would use componentWillUnmount to clean up resources
like event listeners. Example:

componentWillUnmount() {
window.removeEventListener('mousemove', this.logMousePosi

Code evolution React hook 37


tion);
}

🚀 Functional Component Example:


1. Create the MouseTracker Component:

We have a functional component that tracks the mouse position.

2. Problem:

If you toggle the visibility of this component (unmount it), the event
listener stays active, causing warnings about state updates on an
unmounted component.

🖥️ Step-by-Step Solution:
🖱️ MouseContainer Component:
State Setup: We maintain a display state to toggle the visibility of the
MouseTracker component.

import React, { useState } from 'react';


import MouseTracker from './MouseTracker';

const MouseContainer = () => {


const [display, setDisplay] = useState(true);

return (
<div>
<button onClick={() => setDisplay(!display)}>
Toggle Display
</button>
{display && <MouseTracker />}
</div>
);
};

export default MouseContainer;

Code evolution React hook 38


Button: Toggles the display state, which controls whether MouseTracker is
rendered.

🖱️ MouseTracker Component with useEffect :


Problem: When the MouseTracker is unmounted, the event listener is still
active, leading to warnings.

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

const MouseTracker = () => {


const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

useEffect(() => {
const logMousePosition = (e) => {
setMousePos({ x: e.clientX, y: e.clientY });
};

// Adding the mouse event listener


window.addEventListener('mousemove', logMousePosition);

// Cleanup: Return a function that will be called on co


mponent unmount
return () => {
console.log('Component unmounting, removing event lis
tener');
window.removeEventListener('mousemove', logMousePosit
ion);
};
}, []); // Empty dependency array ensures this effect run
s only once (on mount)

return (
<div>
X: {mousePos.x}, Y: {mousePos.y}
</div>
);
};

Code evolution React hook 39


export default MouseTracker;

🛠️ Key Concepts in useEffect Cleanup:


1. Event Listener:

In the useEffect hook, we add the mousemove event listener to track


mouse positions.

2. Cleanup Function:

To prevent memory leaks, we return a cleanup function inside useEffect .


This function is executed when the component unmounts or before the
next effect run.

3. Empty Dependency Array ( [] ):

The effect runs only once, similar to componentDidMount , since the array is
empty. The cleanup will occur when the component is unmounted.

💡 Takeaways:
Cleanup with useEffect : Always return a cleanup function when working
with side effects that require resource management (e.g., event listeners,
subscriptions).

Memory Leak Prevention: Failing to clean up event listeners or


subscriptions will lead to memory leaks, as events continue firing even after
the component is unmounted.

Return Cleanup from useEffect : The cleanup function allows you to manage
state or subscriptions when the component unmounts, similar to
componentWillUnmount in class components.

📝 Optimized Code Recap:


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

const MouseTracker = () => {


const [mousePos, setMousePos] = useState({ x: 0, y: 0 });

Code evolution React hook 40


useEffect(() => {
// Mouse position logger
const logMousePosition = (e) => {
setMousePos({ x: e.clientX, y: e.clientY });
};

// Adding event listener


window.addEventListener('mousemove', logMousePosition);

// Cleanup: Removing event listener


return () => {
console.log('Component unmounting, removing event lis
tener');
window.removeEventListener('mousemove', logMousePosit
ion);
};
}, []); // Run only once (on mount)

return (
<div>
X: {mousePos.x}, Y: {mousePos.y}
</div>
);
};

export default MouseTracker;

🔑 Conclusion:
Cleanup is essential for managing resources in functional
useEffect

components, preventing memory leaks.

Returning a cleanup function inside useEffect ensures that event listeners or


subscriptions are removed when the component is unmounted.

Code evolution React hook 41


📚 Detailed Notes on Common Mistake with useEffect and
Interval Timer
In this video, we focus on a common mistake beginners might make when
working with useEffect —especially when transitioning from class components
to functional components. The mistake is related to dependencies in the
useEffect hook.

🧑‍💻 Objective:
Create an interval counter that automatically increments every second.

Replicate the class component lifecycle behavior using functional


components with hooks.

Understand the common mistake regarding the dependency array in


useEffect .

🏗️ Class Component Implementation:


1. State: The count variable is initialized to 0 in the constructor.

2. Interval Setup: setInterval is used in componentDidMount to increment the


count by 1 every second.

3. Cleanup: clearInterval is used in componentWillUnmount to clean up the timer


and prevent memory leaks.

class IntervalClassCounter extends React.Component {


constructor(props) {
super(props);
this.state = { count: 0 };
}

componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}

componentWillUnmount() {
clearInterval(this.interval);
}

Code evolution React hook 42


tick = () => {
this.setState((prevState) => ({
count: prevState.count + 1
}));
};

render() {
return <div>Count: {this.state.count}</div>;
}
}

🚀 Functional Component with useEffect (Initial Attempt):


Next, we attempt to implement the same functionality using hooks and
functional components.

1. State Setup: Use useState to create the count variable initialized to 0 .

2. Interval Setup: Use setInterval to increment the count value every second,
simulating componentDidMount .

3. Cleanup: Use useEffect to clear the interval timer on unmount, replicating


componentWillUnmount .

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

const IntervalHookCounter = () => {


const [count, setCount] = useState(0);

const tick = () => {


setCount(count + 1);
};

useEffect(() => {
const interval = setInterval(tick, 1000);

return () => {
clearInterval(interval); // Cleanup
};
}, []); // Empty dependency array

Code evolution React hook 43


return <div>Count: {count}</div>;
};

🧐 Problem:
The counter doesn't increment as expected. It shows the value 1 but
doesn't update every second.

What went wrong? Let's dive into the reason.

⚠️Explanation of the Mistake:


1. Mental Model Issue: We treated the empty dependency array ( [] ) in
useEffectas a direct equivalent to componentDidMount in class components.
This led to incorrect assumptions.

2. Dependency Array Misconception:

The empty array ( [] ) means the effect runs only once—on the initial
render.

But state updates (like the setCount call inside the tick function) do not
trigger the effect again. This is because we didn't list count as a
dependency.

3. Result:

The initial render sets the count to 0 .

tick increments count to 1 , but the effect doesn't run again, so the
interval doesn't keep updating the state as expected.

🔧 Solution:
1. Fix: Add count to the dependency array. This ensures that the setInterval

function runs again when count changes.

useEffect(() => {
const interval = setInterval(tick, 1000);

return () => {
clearInterval(interval); // Cleanup

Code evolution React hook 44


};
}, [count]); // Dependency array with count

1. Alternative Fix: Use the functional update form of setCount . This avoids the
need to include count as a dependency.

const tick = () => {


setCount((prevCount) => prevCount + 1);
};

useEffect(() => {
const interval = setInterval(tick, 1000);

return () => {
clearInterval(interval); // Cleanup
};
}, []); // No need to add count to the dependency array

Why this works: The functional update form of setCount allows React to
keep track of the previous state ( prevCount ), and we no longer need to
watch for changes in the count state.

💡 Key Takeaways:
1. Dependency Array:

If you need to use a value (like count ) inside useEffect , include it in the
dependency array, or else the effect may not run as expected.

Empty array ( [] ) means the effect runs only once on the initial render.

2. Functional Update with setState :

When using setState inside useEffect , consider using the functional


update form to access the previous state, avoiding the need to include
the state variable in the dependency array.

3. Common Mistake:

Beginners often make the mistake of thinking the empty dependency


array in useEffect works exactly like componentDidMount . It doesn’t trigger
when state changes, so be mindful when using state inside useEffect .

Code evolution React hook 45


📑 Code Recap:
🛠️ Final Corrected Code (With Count as Dependency):
import React, { useState, useEffect } from 'react';

const IntervalHookCounter = () => {


const [count, setCount] = useState(0);

const tick = () => {


setCount(count + 1);
};

useEffect(() => {
const interval = setInterval(tick, 1000);

return () => {
clearInterval(interval); // Cleanup
};
}, [count]); // Dependency array with count

return <div>Count: {count}</div>;


};

🛠️ Final Corrected Code (Using Functional setState ):

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

const IntervalHookCounter = () => {


const [count, setCount] = useState(0);

const tick = () => {


setCount((prevCount) => prevCount + 1); // Functional u
pdate
};

useEffect(() => {
const interval = setInterval(tick, 1000);

Code evolution React hook 46


return () => {
clearInterval(interval); // Cleanup
};
}, []); // No need for count in dependency array

return <div>Count: {count}</div>;


};

🔑 Conclusion:
State management and dependencies in useEffect can be tricky for
beginners, especially when migrating from class components.

Remember: The dependency array is not just about when to rerun the
effect—it's about tracking changes to values used in the effect.

Use the functional update form of setState when you need to avoid state
dependencies in the effect.

📚 Detailed Notes on Data Fetching with useEffect and Axios


In this video, the focus is on applying the state and effect hooks to fetch data
from an API endpoint. Although React Suspense will be the future solution for
data fetching, this method using hooks is a great way to solidify your
understanding.

🧑‍💻 Objective:
Fetch data from an API endpoint (using Axios) and render it in the UI.

Understand how to manage state and side effects when working with
external data sources.

Apply useEffect to trigger the data fetching only once when the component
mounts.

🏗️ Step-by-Step Implementation:
Code evolution React hook 47
1. Install Axios:
Axios is used here for making HTTP requests. While the Fetch API is a
native browser API, Axios simplifies error handling and provides a more
readable syntax.

npm install axios

After installation, the axios package will be listed in your package.json file.

2. Component Setup:
Create a new file dataFetching.js in your project folder.

Use RFC (React Function Component) snippet to create a functional


component.

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


import axios from 'axios';

3. Fetch Data from the API:


API: Use JSONPlaceholder for testing the data fetching.

The URL https://jsonplaceholder.typicode.com/posts provides an array of 100


posts.

4. State Setup:
Create a state variable posts using the useState hook. Initialize it with an
empty array [] to store the fetched data.

const [posts, setPosts] = useState([]);

5. Fetching Data with useEffect :


Inside useEffect , use Axios to make a GET request to the API.

When the request is successful, update the state using setPosts .

If there’s an error, log it to the console.

Code evolution React hook 48


useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
setPosts(response.data); // Store data in state
})
.catch(error => {
console.log("Error fetching data: ", error);
});
}, []); // Empty dependency array ensures this runs only on
ce (on mount)

6. Rendering the Data:


After successfully fetching the data and updating the state, map over the
posts array and render each post title in an unordered list.

return (
<div>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);

⚠️Problem Encountered:
After fetching the data and logging the response to the console, we notice
that the UI doesn’t display the fetched posts.

Issue: The state was not being updated after fetching data.

Solution: We added setPosts(response.data) to update the state, and this fixed


the issue, allowing posts to render in the UI.

🌀 Infinite Loop Issue:

Code evolution React hook 49


Problem: After fixing the UI rendering, an infinite loop of data fetching
occurred.

Cause: This happens because the useEffect was being triggered on every
render. By default, useEffect runs after every render cycle, and since we
were updating the state in setPosts , it caused re-renders that triggered
another API request.

Solution: Add an empty dependency array ( [] ) as the second argument to


useEffect . This ensures that the effect runs only once, similar to

componentDidMount in class components.

useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
setPosts(response.data);
})
.catch(error => {
console.log("Error fetching data: ", error);
});
}, []); // Empty dependency array ensures data is fetched o
nly once

🔧 Code Recap:
import React, { useState, useEffect } from 'react';
import axios from 'axios';

const DataFetching = () => {


const [posts, setPosts] = useState([]);

useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(response => {
setPosts(response.data); // Update state with fetch
ed data
})
.catch(error => {
console.log("Error fetching data: ", error);

Code evolution React hook 50


});
}, []); // Empty dependency array to fetch data only once
on mount

return (
<div>
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
};

export default DataFetching;

💡 Key Takeaways:
1. State Management:

Use useState to store the data fetched from an API.

Always update state with the data once the request is successful to
trigger a re-render.

2. Handling Side Effects with useEffect :

Use useEffect to fetch data, as it works like the lifecycle methods in


class components.

Add an empty dependency array ( [] ) to useEffect to ensure that the


effect runs only once, preventing infinite loops.

3. Error Handling:

Always catch errors from HTTP requests (with .catch ) to avoid


unhandled promise rejections.

4. Axios vs Fetch API:

Axios simplifies error handling and is often preferred over the Fetch API
due to its ability to handle both HTTP errors and network errors more

Code evolution React hook 51


elegantly.

📑 Next Steps:
In the next video, we will expand on this example and learn how to fetch
individual posts by entering a post ID and making the request with a button
click.

📚 Detailed Notes on Fetching Individual Posts by Post ID


In this video, we learn how to fetch individual posts from an API by passing the
post ID to the GET request.
The JSONPlaceholder API allows us to fetch all posts with /posts , and
individual posts with /posts/{id} , where {id} represents the post ID.

🧑‍💻 Objective:
Fetch individual posts by appending the post ID to the URL endpoint.

Handle input from the user to dynamically change the post ID and trigger a
new API request.

Improve the solution by triggering the API request only on a button click,
instead of on every keystroke.

🏗️ Step-by-Step Implementation:
1. Fetching an Individual Post by ID:
To fetch a single post, the API endpoint will be:

/posts/{id}

Example: /posts/1 for post with ID 1.

2. Create an Input Field for Post ID:


Add an input element to let the user enter the post ID.

The input is a controlled component, meaning we manage its value


through React state.

const [id, setId] = useState(1); // Initialize state with


1

Code evolution React hook 52


// JSX for input field
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)} // Update state o
n change
/>

3. Change API Request to Fetch Individual Post:


Update the API request URL to dynamically fetch the post based on the
entered ID.

Use template literals to append the post ID.

const [post, setPost] = useState({}); // Initialize post as


an empty object

useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${i
d}`)
.then(response => {
setPost(response.data); // Set the fetched post data
})
.catch(error => {
console.log("Error fetching post: ", error);
});
}, [id]); // Dependency array now includes `id` so effect r
uns when ID changes

4. Rendering the Single Post:


In JSX, render the title of the single post fetched based on the id .

return (
<div>
<input
type="text"

Code evolution React hook 53


value={id}
onChange={(e) => setId(e.target.value)} // Handle ID
change
/>
<h3>{post.title}</h3> {/* Render the title of the post
*/}
</div>
);

5. Testing:
Initially, the post with ID 1 will be displayed.

When the user changes the ID (e.g., 2 or 10 ), the new post will be fetched
and displayed.

⚠️Issue with Instant Data Fetching:


Problem: On every character typed in the input field, the useEffect is
triggered, causing frequent API requests.

Cause: The useEffect hook is triggered each time the id changes, which
leads to re-fetching the data on each keystroke.

Solution: To solve this, debouncing or using a button click to trigger the


request would be a better approach. In this video, the solution is discussed
for the next steps.

🛠️ Improvement: Trigger API Request on Button Click:


Problem: The current approach triggers the effect with every change in the
input field.

Solution: Move the request to a button click handler so the effect is only
triggered when the user is ready (e.g., after they press a button).

Here’s how you can modify the code to trigger the request only when a button
is clicked:

1. Add a Button to Trigger Fetch:

Create a button element and handle its onClick event to trigger the API
request.

Code evolution React hook 54


const handleFetchPost = () => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${i
d}`)
.then(response => {
setPost(response.data); // Set post data
})
.catch(error => {
console.log("Error fetching post: ", error);
});
};

return (
<div>
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)} // Update ID
on change
/>
<button onClick={handleFetchPost}>Fetch Post</button>
{/* Trigger on button click */}
<h3>{post.title}</h3>
</div>
);

1. Explanation:

The button click will call the handleFetchPost function.

Inside handleFetchPost , the axios request is triggered to fetch the post.

💡 Key Takeaways:
1. Dynamic Fetching by ID:

Use template literals to dynamically build the URL for fetching an


individual post.

Update the state ( setPost ) when the data is fetched.

2. Controlled Input:

Code evolution React hook 55


Use useState for controlled components like the input field, allowing the
user to modify the post ID.

3. Efficient Data Fetching:

Avoid making API requests on every keystroke by using debouncing or


triggering the request with a button click.

📝 Final Code Recap (with Button Click Trigger):


import React, { useState } from 'react';
import axios from 'axios';

const DataFetchingById = () => {


const [id, setId] = useState(1); // Initial ID is 1
const [post, setPost] = useState({}); // Store the fetche
d post data

const handleFetchPost = () => {


axios.get(`https://jsonplaceholder.typicode.com/posts/
${id}`)
.then(response => {
setPost(response.data); // Set fetched post data
})
.catch(error => {
console.log("Error fetching post: ", error);
});
};

return (
<div>
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)} // Update s
tate on change
/>
<button onClick={handleFetchPost}>Fetch Post</button>
{/* Trigger fetch on button click */}

Code evolution React hook 56


<h3>{post.title}</h3> {/* Render the title of the fet
ched post */}
</div>
);
};

export default DataFetchingById;

🔧 Next Steps:
In the next video, we will discuss how to improve the UI/UX, possibly with
loading states or error handling for a more polished experience.

📚 Detailed Notes on Triggering Effect with Button Click


In this video, we learn how to trigger data fetching using the effect hook on a
button click instead of immediately fetching data on each input change.

🧑‍💻 Objective:
Trigger an API request based on a button click instead of using the
onChange event for every keystroke in the input field.

Use the useEffect hook in a way that it only re-fetches data when the button
is clicked, not on each change of the input value.

🏗️ Step-by-Step Implementation:
1. Create State Variables:
We will create two state variables:

1. One for tracking the ID entered by the user ( id ).

2. Another for storing the post ID that will trigger the useEffect hook
( idFromButtonClick ).

const [id, setId] = useState(1); // Initial value set to 1


for the ID

Code evolution React hook 57


const [idFromButtonClick, setIdFromButtonClick] = useState
(1); // To track post ID when button is clicked

2. Add an Input Field and Button:


The input field will accept the post ID, and the button will trigger the effect
when clicked.

return (
<div>
<input
type="text"
value={id} // Controlled input for post ID
onChange={(e) => setId(e.target.value)} // Update id
on input change
/>
<button onClick={handleClick}>Fetch Post</button> {/* B
utton to trigger fetch */}
</div>
);

3. Handle Button Click:


Create a handleClick function that updates the idFromButtonClick state based
on the value entered in the input field. This will trigger the useEffect hook.

const handleClick = () => {


setIdFromButtonClick(id); // Update idFromButtonClick wit
h the value of `id` when button is clicked
};

4. Modify useEffect to Trigger on idFromButtonClick :


Instead of using id directly, we now want the useEffect to depend on
idFromButtonClick .

This ensures the data is fetched only when the button is clicked.

Code evolution React hook 58


useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${i
dFromButtonClick}`)
.then(response => {
setPost(response.data); // Update the post data
})
.catch(error => {
console.log("Error fetching post: ", error);
});
}, [idFromButtonClick]); // Effect depends on `idFromButton
Click`

5. Rendering the Post:


Display the post's title in the component.

return (
<div>
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)} // Controlled
input
/>
<button onClick={handleClick}>Fetch Post</button> {/* F
etch on button click */}
<h3>{post.title}</h3> {/* Display the fetched post's ti
tle */}
</div>
);

⚠️Key Points:
1. State Management:

id state is used to track the user input for the post ID.

idFromButtonClickstate is used to trigger the useEffect hook when the


button is clicked.

Code evolution React hook 59


2. Button Click as the Trigger:

The button click updates idFromButtonClick , which is used in the useEffect

dependency array.

This approach ensures the API is fetched only after the button is
clicked, instead of fetching on every input change.

3. Effect Hook:

The useEffect hook is dependent on idFromButtonClick , not the immediate


input value ( id ). This makes the data fetch action controlled and
efficient.

🧪 Testing:
Initially, the first post is shown (since the default ID is 1 ).

Changing the input to a new value (e.g., 2 , 3 , etc.) does not trigger a
request until the user clicks the "Fetch Post" button.

When the button is clicked, the post for the entered ID is fetched and
displayed.

🛠️ Full Working Code Example:


import React, { useState, useEffect } from 'react';
import axios from 'axios';

const DataFetchingOnButtonClick = () => {


const [id, setId] = useState(1); // ID entered by user
const [idFromButtonClick, setIdFromButtonClick] = useStat
e(1); // ID to trigger useEffect
const [post, setPost] = useState({}); // Store the fetche
d post

const handleClick = () => {


setIdFromButtonClick(id); // Set the idFromButtonClick
value when button is clicked
};

// Fetch data when idFromButtonClick changes

Code evolution React hook 60


useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/
${idFromButtonClick}`)
.then(response => {
setPost(response.data); // Set the fetched post dat
a
})
.catch(error => {
console.log("Error fetching post: ", error);
});
}, [idFromButtonClick]); // Dependency on idFromButtonCli
ck

return (
<div>
<input
type="text"
value={id}
onChange={(e) => setId(e.target.value)} // Controll
ed input
/>
<button onClick={handleClick}>Fetch Post</button> {/*
Button triggers data fetch */}
<h3>{post.title}</h3> {/* Render the title of the fet
ched post */}
</div>
);
};

export default DataFetchingOnButtonClick;

💡 Key Takeaways:
1. Button Click Triggers Effect: By using a button click to set the state that the
useEffect depends on, you control when the data is fetched.

2. Controlled Input: The input field is controlled, with the id state being
updated based on the user's input.

Code evolution React hook 61


3. Optimized Data Fetching: The data is only fetched when necessary (i.e.,
after the button click), reducing unnecessary API calls.

📚 Understanding React Context API


In this video, we learn about the React Context API, which helps pass data
through the component tree without manually passing props down at every
level. Let's break down the problem and solution provided by the Context API.

🧑‍💻 Problem: Passing Props Deeply in a Component Tree


Imagine you have a React application with several nested components:

App (root component)

Component A

Component B

Component D (nested in B)

Component C

Component E

Component F (nested in E)

Requirement:
You need to display the logged-in username in Components A, D, and F.

The username is stored as a prop in the App component.

Traditional Prop Drilling Issue:


Component A can directly receive the username via props, but for
Component D, the prop needs to be passed from App ➡ ➡B D .

Similarly, for Component F, the prop needs to be passed through ➡



C E

F .

If your component tree is deeply nested (e.g., 5–10 levels deep), this prop
drilling becomes tedious and problematic, especially when the data (like a

Code evolution React hook 62


logged-in username or language preference) needs to be accessed in many
components.

The challenge is:


Intermediate components like B , C , and `E** don't need the prop but must
still pass it down.

The more levels you add, the more you need to pass props manually,
leading to "prop drilling".

⚡ Solution: React Context API


Context API Overview:
React Context provides a way to pass data directly to the components that
need it, skipping intermediate components.

Instead of passing props manually at every level, Context lets you share
data across the component tree without the need to manually forward
props through every layer.

Example Use Cases:


1. User Authentication: Passing logged-in user data across many
components.

2. Language Preferences: Sharing the user's language setting across


different components.

3. UI Themes: Passing global UI state (e.g., dark mode, light mode).

🔍 Key Concept of Context:


Context allows you to create a global state that can be accessed by any
component in your application, no matter how deep it is nested.

How it Works:
1. Create a Context:

A Context is created using React.createContext() .

2. Provide Context:

Code evolution React hook 63


The Provider component wraps the part of your component tree where
you want to share data.

Any component inside the Provider can access the data.

3. Consume Context:

The components that need access to the shared data use the Consumer

component or the useContext hook (in newer React versions).

💡 Why Context Helps:


Avoids Prop Drilling: You no longer need to pass props down manually at
every level.

Global State Management: Easily manage state that needs to be accessible


across many components, like a logged-in user or UI preferences.

Cleaner Code: Reduces the need for deeply nested components that
simply pass data down.

🛠️ What’s Next?:
In the next video, we will explore how Context API was used before the
introduction of hooks. This will help us understand the improvements and
benefits brought by the useContext hook.

🧑‍💻 Key Takeaways:


Context API solves the problem of prop drilling by allowing you to share
data across components without passing it down through every level.

It is especially useful for global data that needs to be accessed by many


components, like authenticated user information, language preferences,
or theme settings.

You're right to point that out! In the code for consuming multiple contexts, I
missed the return statement in the nested render props pattern. Let me correct
that and show you the correct version of the code.

Correct Code for Consuming Multiple Contexts:

Code evolution React hook 64


When consuming multiple contexts using the render props pattern, you need
to return the JSX inside each consumer.
Here’s the corrected version:

// Step 1: Create Contexts


const userContext = React.createContext();
const channelContext = React.createContext();

// App Component
const App = () => {
return (
<userContext.Provider value="Vishwas">
<channelContext.Provider value="Code Evolution">
<ComponentC />
</channelContext.Provider>
</userContext.Provider>
);
};

// Component C
const ComponentC = () => {
return <ComponentE />;
};

// Component E
const ComponentE = () => {
return <ComponentF />;
};

// Component F - Corrected Code: Consume Multiple Contexts


const ComponentF = () => {
return (
<userContext.Consumer>
{userValue => (
<channelContext.Consumer>
{channelValue => (
<div>{userValue} is on {channelValue}</div> {/
* Output: Vishwas is on Code Evolution */}

Code evolution React hook 65


)}
</channelContext.Consumer>
)}
</userContext.Consumer>
);
};

What's Fixed:
Return Statements: The JSX inside the consumers must be returned for the
proper rendering to happen. In the previous example, I missed the actual
return keyword inside the consumers.

Expected Output:
In Component F, we should now correctly display the message: Vishwas is on

Code Evolution .

This updated code should work properly! Thanks for pointing it out.

🚀 Using the useContext Hook in React –


Step-by-Step Guide
The hook is a powerful way to simplify consuming context values in

useContext

React. Let’s break it down with clear steps, code snippets, and explanations.

🌟 What We Did Before Hooks


Previously, consuming context required the render props pattern, which
involved:

1. Creating a context using createContext .

2. Providing a context value high in the component tree using the <Provider>

component.

Code evolution React hook 66


3. Consuming the context value via a render prop or a static contextType

property.

🔥 Simplifying Context Consumption with useContext


With useContext , you only need a few steps to consume context values. Here's
the modern approach:

📚 Step-by-Step Implementation
1️⃣ Create a Context
import React, { createContext } from 'react';

// Create contexts
export const UserContext = createContext();
export const ChannelContext = createContext();

2️⃣ Provide Context Values


Wrap your component tree with context providers at a high level, passing the
values you want to share.

import React from 'react';


import { UserContext, ChannelContext } from './MyContexts';
import ComponentE from './ComponentE';

function App() {
return (
<UserContext.Provider value="John Doe">
<ChannelContext.Provider value="React Tutorials">
<ComponentE />
</ChannelContext.Provider>
</UserContext.Provider>
);
}

export default App;

Code evolution React hook 67


3️⃣ Consume Context Values with useContext
In the consuming component, use useContext to simplify access to the context
values.

import React, { useContext } from 'react';


import { UserContext, ChannelContext } from './MyContexts';

function ComponentE() {
// Access context values
const user = useContext(UserContext);
const channel = useContext(ChannelContext);

return (

👤 User: {user}</h1>
<div>

<h2>📺 Channel: {channel}</h2>


<h1>

</div>
);
}

export default ComponentE;

🖥️ Output in the Browser


When you run the app, you'll see:

👤 User: John Doe


📺 Channel: React Tutorials
💡 Why Use useContext ?
🚀 Simpler Syntax: Avoids verbose render props or properties.
contextType

🛠️ More Readable: Directly use context values without nesting.


⚡ Functional Component Friendly: Works seamlessly with other hooks.

Code evolution React hook 68


🔧 Improvements
TypeScript: Add strong typing for context values to catch bugs early.

Dynamic Values: Use state or reducers to dynamically update context


values.

Multiple Contexts: Combine multiple context values if required for complex


components.

💬 Closing Thoughts
useContext is a simple yet powerful hook.

It’s ideal for most scenarios where state sharing across components is
necessary.

Practice and implement it wherever applicable! 🌟

🚀 Introduction to useReducer Hook in


React – A Comprehensive Guide
The useReducer hook is a powerful tool in React for state management. It
provides an alternative to useState and is especially helpful for managing
complex state logic. Let’s break down the concepts, including reducers in
JavaScript, and compare them with useReducer .

🌟 Overview of useReducer
useReducer is a React Hook designed for managing state transitions.

It’s an alternative to useState , particularly useful for:

Complex state logic involving multiple sub-values.

Scenarios where the next state depends on the previous state.

Core Idea: useReducer is based on the concept of reducers, commonly used


in functional programming (and Redux).

Code evolution React hook 69


📚 Reducers in JavaScript
To understand useReducer , we need to first grasp JavaScript reducers.

1️⃣ What is a Reducer Function?


A reducer is a function that processes an input (e.g., an array) and reduces
it to a single value.

It’s commonly used with Array.prototype.reduce .

2️⃣ Example of reduce Method

const numbers = [1, 2, 3, 4];

// Reducer function
const reducer = (accumulator, currentValue) => accumulator
+ currentValue;

// Reduce method with an initial value of 0


const total = numbers.reduce(reducer, 0);

console.log(total); // Output: 10

🔑 Key Points:
reduce Parameters:

First: A reducer function.

Second: An initial value for the accumulator.

Reducer Function Parameters:

accumulator : Holds the accumulated result.

currentValue : The current element in the array.

Returns a single reduced value.

🌟 Connection to useReducer
The useReducer hook uses a similar concept but applies it to React state
management.

Code evolution React hook 70


🔑 Key Differences:
Array Reducer React useReducer

Processes array elements Processes state transitions

Returns a single value Returns state and a dispatch function

reduce has accumulator and current value useReducer has state and action

📚 Syntax of useReducer

const [state, dispatch] = useReducer(reducer, initialStat


e);

Parameters:
1. Reducer Function: (state, action) => newState

Processes the current state and an action to return the next state.

2. Initial State: The starting value of the state.

Return Value:
state : The current state value.

dispatch : A function to trigger actions.

🛠️ Example: Counter with useReducer


Let’s see a simple example of useReducer .

Step 1: Define the Reducer Function

const reducer = (state, action) => {


switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:

Code evolution React hook 71


throw new Error('Unknown action type');
}
};

Step 2: Use useReducer in a Component

import React, { useReducer } from 'react';

function Counter() {
// Initialize the reducer with the reducer function and i
nitial state
const [state, dispatch] = useReducer(reducer, { count: 0
});

return (
<div>
<h1>Counter: {state.count}</h1>

➕ Increment</button>
<button onClick={() => dispatch({ type: 'increment'
})}>

})}>➖ Decrement</button>
<button onClick={() => dispatch({ type: 'decrement'

>🔄 Reset</button>
<button onClick={() => dispatch({ type: 'reset' })}

</div>
);
}

export default Counter;

🖥️ Output in Browser:
➕ ➖ Decrement] [🔄 Reset]
Counter: 0
[ Increment] [

💡 Key Concepts:
Actions: Represent what should happen (e.g., 'increment' , 'decrement' ).

Code evolution React hook 72


Dispatch: Triggers the action.

Reducer: Handles state transitions based on the current state and the
action.

⚡ When to Use useReducer vs useState

Use useState Use useReducer

Simple state (e.g., a single counter). Complex state with multiple sub-values.

State updates are independent. State transitions depend on the previous state.

Minimal logic for state updates. Complex logic for state updates.

🔧 Improvements and Tips:


Combine Reducers: For large apps, split reducers into smaller ones and
combine them.

Middleware: Use middleware-like functions to extend reducer capabilities.

Memoization: Optimize with useMemo if the reducer logic is computationally


expensive.

🌟 Summary
useReducer is a hook for state management in React.

Works well for complex state transitions.

Similar to the reduce method in JavaScript.

Returns a state and a dispatch function.

🎯 With this foundation, dive into practical examples of useReducer to deepen


your understanding!

🚀 Detailed Notes: Understanding useReducer with a Counter


Example

Introduction

Code evolution React hook 73


In this example, we implement a counter using the useReducer hook. The goal is
to:

Increment the count value.

Decrement the count value.

Reset the count value to 0.

This example is a step forward in understanding useReducer , offering a direct


comparison with how we handled state using useState .

Steps to Implement the Counter with useReducer

Step 1: Setup the Component


1. Create a new file named CounterOne.js .

2. Use the snippet rafce (React Functional Component Export) to create a


boilerplate component.

import React, { useReducer } from "react";

const CounterOne = () => {


return (
<div>
<h1>Counter</h1>
<div>Count: 0</div>
<button>Increment</button>
<button>Decrement</button>
<button>Reset</button>
</div>
);
};

export default CounterOne;

Step 2: Understand useReducer


The useReducer hook is used for managing state in React. It requires:

1. A reducer function – Responsible for state transitions.

Code evolution React hook 74


2. Initial state – The starting value of the state.

Step 3: Define the Reducer and Initial State


1. Define an initial state for the counter.

const initialState = 0;

1. Define the reducer function. It:

Accepts state (current state) and action (instruction).

Returns the new state based on the action.

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}
};

Step 4: Use useReducer in the Component


Call useReducer and destructure the returned state ( count ) and the dispatch

method.

const [count, dispatch] = useReducer(reducer, initialStat


e);

Step 5: Add Logic to JSX


1. Display the count in the JSX.

2. Use the dispatch method to trigger actions for each button.

Code evolution React hook 75


return (
<div>
<h1>Counter</h1>
<div>Count: {count}</div>
<button onClick={() => dispatch("increment")}>Incre
ment</button>
<button onClick={() => dispatch("decrement")}>Decre
ment</button>
<button onClick={() => dispatch("reset")}>Reset</bu
tton>
</div>
);

Final Code

import React, { useReducer } from "react";

const initialState = 0;

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}
};

const CounterOne = () => {


const [count, dispatch] = useReducer(reducer, initialSt
ate);

return (

Code evolution React hook 76


<div>
<h1>Counter</h1>
<div>Count: {count}</div>
<button onClick={() => dispatch("increment")}>I
ncrement</button>
<button onClick={() => dispatch("decrement")}>D
ecrement</button>
<button onClick={() => dispatch("reset")}>Reset
</button>
</div>
);
};

export default CounterOne;

🧠 How It Works
1. useReducer Hook:

reducer processes the state and action .

The returned count is the current state.

The dispatch function triggers the reducer with a specific action.

2. Reducer Logic:

increment : Adds 1 to state .

decrement : Subtracts 1 from state .

reset : Sets state to initialState .

3. Button Clicks:

Each button invokes dispatch with a specific action ( "increment" ,


"decrement" , "reset" ).

✨ Comparison with useState


useState is ideal for simple state changes.

useReducer shines when:

State transitions are complex.

Code evolution React hook 77


The state depends on previous state and multiple actions.

🚀 Improvements & Tips


1. Action as Object:

Replace action strings with objects for more flexibility.

dispatch({ type: "increment" });


dispatch({ type: "decrement" });
dispatch({ type: "reset" });

1. Combine Multiple States:

Use useReducer for managing multiple states efficiently in a single


reducer.

This implementation is perfect for beginners transitioning to state management


in React. Dive deeper by integrating this with Redux-like patterns! 🎉

🚀 Detailed Notes: useReducer with State and Action as


Objects

Introduction
In this example, we enhance the useReducer usage by converting:

State from a simple value to an object.

Action from a string to an object.

This approach mirrors the pattern often seen in Redux and provides flexibility
for:

1. Passing additional data via actions.

2. Managing multiple state variables with a single reducer.

Steps to Implement useReducer with Objects

Code evolution React hook 78


Step 1: Setup the Component
1. Create a new file named CounterTwo.js .

2. Copy the code from the previous example ( CounterOne ) and update the
component name to CounterTwo .

import React, { useReducer } from "react";

const CounterTwo = () => {


return (
<div>
<h1>Counter</h1>
<div>First Counter: 0</div>
<button>Increment</button>
<button>Decrement</button>
<button>Reset</button>
</div>
);
};

export default CounterTwo;

Step 2: Update the State to an Object


1. Define the initial state as an object:

Add firstCounter as a property initialized to 0 .

const initialState = {
firstCounter: 0,
};

1. Update the JSX to use count.firstCounter instead of count .

Step 3: Update the Reducer Function


1. Modify the reducer function:

Return an object for each state update.

Code evolution React hook 79


Use action.type instead of action to handle switch cases.

const reducer = (state, action) => {


switch (action.type) {
case "increment":
return { ...state, firstCounter: state.firstCou
nter + 1 };
case "decrement":
return { ...state, firstCounter: state.firstCou
nter - 1 };
case "reset":
return initialState;
default:
return state;
}
};

Step 4: Update the Dispatch Actions


1. Modify the dispatch calls in button handlers:

Use objects with type property.

<button onClick={() => dispatch({ type: "increment" })}>Inc


rement</button>
<button onClick={() => dispatch({ type: "decrement" })}>Dec
rement</button>
<button onClick={() => dispatch({ type: "reset" })}>Reset</
button>

Improvement 1: Adding Increment/Decrement by Custom


Values

Step 1: Extend Action with Additional Properties


1. Add a value property to the dispatch action objects.

Code evolution React hook 80


<button onClick={() => dispatch({ type: "increment", value:
1 })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement", value:
1 })}>Decrement</button>
<button onClick={() => dispatch({ type: "increment", value:
5 })}>Increment by 5</button>
<button onClick={() => dispatch({ type: "decrement", value:
5 })}>Decrement by 5</button>

Step 2: Modify Reducer to Use action.value


Update the reducer function to use action.value instead of hardcoding 1 .

const reducer = (state, action) => {


switch (action.type) {
case "increment":
return { ...state, firstCounter: state.firstCou
nter + action.value };
case "decrement":
return { ...state, firstCounter: state.firstCou
nter - action.value };
case "reset":
return initialState;
default:
return state;
}
};

Improvement 2: Managing Multiple State Variables

Step 1: Add Another Counter


1. Extend initialState with another property, secondCounter .

const initialState = {
firstCounter: 0,

Code evolution React hook 81


secondCounter: 10,
};

1. Add corresponding buttons in the JSX for the second counter.

<div>Second Counter: {count.secondCounter}</div>


<button onClick={() => dispatch({ type: "incrementTwo", val
ue: 1 })}>Increment Counter 2</button>
<button onClick={() => dispatch({ type: "decrementTwo", val
ue: 1 })}>Decrement Counter 2</button>

Step 2: Update Reducer for Second Counter


Add incrementTwo and decrementTwo cases to the reducer.

const reducer = (state, action) => {


switch (action.type) {
case "increment":
return { ...state, firstCounter: state.firstCou
nter + action.value };
case "decrement":
return { ...state, firstCounter: state.firstCou
nter - action.value };
case "incrementTwo":
return { ...state, secondCounter: state.secondC
ounter + action.value };
case "decrementTwo":
return { ...state, secondCounter: state.secondC
ounter - action.value };
case "reset":
return initialState;
default:
return state;
}
};

Final Code

Code evolution React hook 82


import React, { useReducer } from "react";

const initialState = {
firstCounter: 0,
secondCounter: 10,
};

const reducer = (state, action) => {


switch (action.type) {
case "increment":
return { ...state, firstCounter: state.firstCou
nter + action.value };
case "decrement":
return { ...state, firstCounter: state.firstCou
nter - action.value };
case "incrementTwo":
return { ...state, secondCounter: state.secondC
ounter + action.value };
case "decrementTwo":
return { ...state, secondCounter: state.secondC
ounter - action.value };
case "reset":
return initialState;
default:
return state;
}
};

const CounterTwo = () => {


const [count, dispatch] = useReducer(reducer, initialSt
ate);

return (
<div>
<h1>Counters</h1>
<div>First Counter: {count.firstCounter}</div>
<button onClick={() => dispatch({ type: "increm
ent", value: 1 })}>Increment</button>

Code evolution React hook 83


<button onClick={() => dispatch({ type: "decrem
ent", value: 1 })}>Decrement</button>
<button onClick={() => dispatch({ type: "increm
ent", value: 5 })}>Increment by 5</button>
<button onClick={() => dispatch({ type: "decrem
ent", value: 5 })}>Decrement by 5</button>
<div>Second Counter: {count.secondCounter}</div
>
<button onClick={() => dispatch({ type: "increm
entTwo", value: 1 })}>Increment Counter 2</button>
<button onClick={() => dispatch({ type: "decrem
entTwo", value: 1 })}>Decrement Counter 2</button>
<button onClick={() => dispatch({ type: "reset"
})}>Reset</button>
</div>
);
};

export default CounterTwo;

Key Takeaways
1. State as Object:

Enables managing multiple state variables in a single reducer.

2. Action as Object:

Allows passing additional data (e.g., value ) to the reducer for complex
updates.

3. Scalable Design:

This pattern is well-suited for managing global state or more complex


scenarios.

4. Flexibility:

Easily add more counters or actions by extending the state and reducer .

This example demonstrates the versatility of useReducer for handling complex


state transitions in React. It’s an excellent way to understand patterns that scale

Code evolution React hook 84


for real-world applications! 🎉

🚀 Detailed Notes: Multiple useReducer Hooks

Introduction
In this example, we demonstrate how to manage multiple state variables with
identical transitions using multiple useReducer hooks. This approach simplifies
the code by avoiding:

Merging multiple variables into a single state object.

Creating duplicate cases in the reducer function.

Steps to Implement Multiple useReducer Hooks

Step 1: Setup the Component


1. Create a new file named CounterThree.js .

2. Copy the code from CounterOne.js and rename the component to


CounterThree .

import React, { useReducer } from "react";

const initialState = 0;

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}

Code evolution React hook 85


};

const CounterThree = () => {


const [count, dispatch] = useReducer(reducer, initialSt
ate);

return (
<div>
<h1>Counter 1</h1>
<div>Count: {count}</div>
<button onClick={() => dispatch("increment")}>I
ncrement</button>
<button onClick={() => dispatch("decrement")}>D
ecrement</button>
<button onClick={() => dispatch("reset")}>Reset
</button>
</div>
);
};

export default CounterThree;

Step 2: Add a Second Counter


1. Add another useReducer hook:

Create a second state and dispatch pair, countTwo and dispatchTwo .

const [countTwo, dispatchTwo] = useReducer(reducer, initial


State);

1. Duplicate the JSX for the second counter:

Update the variables for the second counter ( countTwo and dispatchTwo ).

<div>
<h1>Counter 2</h1>
<div>Count: {countTwo}</div>
<button onClick={() => dispatchTwo("increment")}>Increm

Code evolution React hook 86


ent</button>
<button onClick={() => dispatchTwo("decrement")}>Decrem
ent</button>
<button onClick={() => dispatchTwo("reset")}>Reset</but
ton>
</div>

Final Code

import React, { useReducer } from "react";

const initialState = 0;

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}
};

const CounterThree = () => {


const [countOne, dispatchOne] = useReducer(reducer, ini
tialState);
const [countTwo, dispatchTwo] = useReducer(reducer, ini
tialState);

return (
<div>
<h1>Counter 1</h1>
<div>Count: {countOne}</div>
<button onClick={() => dispatchOne("incremen

Code evolution React hook 87


t")}>Increment</button>
<button onClick={() => dispatchOne("decremen
t")}>Decrement</button>
<button onClick={() => dispatchOne("reset")}>Re
set</button>

<h1>Counter 2</h1>
<div>Count: {countTwo}</div>
<button onClick={() => dispatchTwo("incremen
t")}>Increment</button>
<button onClick={() => dispatchTwo("decremen
t")}>Decrement</button>
<button onClick={() => dispatchTwo("reset")}>Re
set</button>
</div>
);
};

export default CounterThree;

Key Takeaways
1. Simplicity:

When multiple state variables have the same transitions, using multiple
useReducer hooks avoids the complexity of combining state into a single
object.

2. Independent State Management:

Each useReducer hook operates independently, ensuring clear and


predictable behavior.

3. Reusability:

The same reducer function can be reused across multiple useReducer

hooks, promoting DRY (Don't Repeat Yourself) principles.

4. Code Readability:

Duplicating JSX for similar components is straightforward and avoids


excessive complexity in the reducer logic.

Code evolution React hook 88


This approach is excellent for scenarios where you need multiple instances of
the same behavior but want to keep their states separate. It’s a clean and
scalable solution for local state management! 🎉

🚀 Detailed Notes: Combining useReducer and useContext for


Global State Management

Introduction
Managing global state in React can become challenging with deeply nested
components. Instead of prop drilling, we combine the power of useReducer and
useContext to share and manage global state efficiently.
This example demonstrates:

1. Creating a global counter in the root component using useReducer .

2. Sharing the counter's state and dispatch method across deeply nested
components using useContext .

Steps to Implement Global State Management

Step 1: Define the Reducer and Initial State


1. Define the initial state:

const initialState = 0;

2. Define the reducer function to manage state transitions:

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;

Code evolution React hook 89


default:
return state;
}
};

Step 2: Create the Global Context


1. Import createContext from React and initialize the context:

import React, { createContext, useReducer } from "reac


t";

export const CountContext = createContext();

Step 3: Set Up the Root Component


1. Use the useReducer hook in the root component ( App.js ):

const App = () => {


const [count, dispatch] = useReducer(reducer, initia
lState);

return (
<CountContext.Provider value={{ countState: coun
t, countDispatch: dispatch }}>
<div>
<h1>Global Counter: {count}</h1>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
</CountContext.Provider>
);
};
export default App;

Key Points:

Code evolution React hook 90


Wrap all nested components with CountContext.Provider .

Provide both count (state) and dispatch methods as the value prop.

Step 4: Consume Context in Nested Components


1. In any nested component (e.g., ComponentA ), use useContext to access the
context values:

import React, { useContext } from "react";


import { CountContext } from "./App";

const ComponentA = () => {


const { countState, countDispatch } = useContext(Cou
ntContext);

return (
<div>
<h2>Component A Count: {countState}</h2>
<button onClick={() => countDispatch("increm
ent")}>Increment</button>
<button onClick={() => countDispatch("decrem
ent")}>Decrement</button>
<button onClick={() => countDispatch("rese
t")}>Reset</button>
</div>
);
};

export default ComponentA;

2. Repeat this in other components (e.g., ComponentD , ComponentF ) to consume


the global state.

Final Folder Structure

src/
├── App.js
├── components/

Code evolution React hook 91


│ ├── ComponentA.js
│ ├── ComponentB.js
│ ├── ComponentC.js
│ ├── ComponentD.js
│ └── ComponentF.js

Final Code for App.js

import React, { createContext, useReducer } from "react";


import ComponentA from "./components/ComponentA";
import ComponentB from "./components/ComponentB";
import ComponentC from "./components/ComponentC";

export const CountContext = createContext();

const initialState = 0;

const reducer = (state, action) => {


switch (action) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
case "reset":
return initialState;
default:
return state;
}
};

const App = () => {


const [count, dispatch] = useReducer(reducer, initialSt
ate);

return (
<CountContext.Provider value={{ countState: count,
countDispatch: dispatch }}>

Code evolution React hook 92


<div>
<h1>Global Counter: {count}</h1>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
</CountContext.Provider>
);
};

export default App;

Final Code for Nested Components

Example: ComponentA.js

import React, { useContext } from "react";


import { CountContext } from "../App";

const ComponentA = () => {


const { countState, countDispatch } = useContext(CountC
ontext);

return (
<div>
<h2>Component A Count: {countState}</h2>
<button onClick={() => countDispatch("incremen
t")}>Increment</button>
<button onClick={() => countDispatch("decremen
t")}>Decrement</button>
<button onClick={() => countDispatch("reset")}>
Reset</button>
</div>
);
};

export default ComponentA;

Code evolution React hook 93


Key Takeaways
1. Prop Drilling Solution:

The useContext API eliminates the need for passing props down multiple
levels, especially in deeply nested component trees.

2. Global State Management:

Combining useReducer and useContext allows centralized management of


state while keeping the state transitions clean.

3. Reusability:

Both countState and countDispatch can be consumed in any component,


avoiding duplication.

4. Scalability:

This pattern is scalable for managing complex global states and can be
extended to multiple contexts if needed.

This example effectively showcases state sharing across components in React


using functional hooks! 🎉
📝 Detailed Notes: Data Fetching with useState Hook 📝
In this example, we explore how to fetch data from an API using the useState

hook combined with useEffect . Here's a step-by-step breakdown of the


process:

🎯 Goals:
Fetch data from an API when the component mounts.

Show a loading indicator while fetching data.

Display the data when the fetch is successful.

Display an error message if the fetch fails.

🔧 Step 1: Install Axios


Axios is used to make HTTP requests.

Code evolution React hook 94


npm install axios

🔧 Step 2: Set Up the Component


1. Create a new file DataFetchingOne.js .

2. Generate a functional component using the rafce snippet or similar.

🔧 Step 3: Import Required Dependencies


import React, { useState, useEffect } from 'react';
import axios from 'axios';

🔧 Step 4: Define State Variables


We need three state variables:

1. loading : To indicate the loading state.

2. error : To store error messages.

3. post : To store the fetched data.

const DataFetchingOne = () => {


const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [post, setPost] = useState({});
};

🔧 Step 5: Use useEffect to Fetch Data


Make an API call when the component mounts using axios.get .

useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts/
1')
.then((response) => {
setLoading(false); // Data fetched
setPost(response.data); // Store the fetched po

Code evolution React hook 95


st
setError(''); // Clear any existing errors
})
.catch(() => {
setLoading(false); // Stop loading
setPost({}); // Clear the post data
setError('Something went wrong'); // Display er
ror message
});
}, []); // Empty dependency array ensures the API call happ
ens only once

🔧 Step 6: Render the UI


The UI changes based on the state variables:

Show "Loading" while data is being fetched.

Show the post title if the fetch is successful.

Show an error message if the fetch fails.

return (
<div>
{loading ? (
'Loading...'
) : error ? (
<p>{error}</p>
) : (
<h1>{post.title}</h1>
)}
</div>
);

💡 Complete Code
import React, { useState, useEffect } from 'react';
import axios from 'axios';

Code evolution React hook 96


const DataFetchingOne = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [post, setPost] = useState({});

useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/pos
ts/1')
.then((response) => {
setLoading(false);
setPost(response.data);
setError('');
})
.catch(() => {
setLoading(false);
setPost({});
setError('Something went wrong');
});
}, []);

return (
<div>
{loading ? (
'Loading...'
) : error ? (
<p>{error}</p>
) : (
<h1>{post.title}</h1>
)}
</div>
);
};

export default DataFetchingOne;

✅ Testing the Component


1. Add DataFetchingOne to your main App.js or another component:

Code evolution React hook 97


import React from 'react';
import DataFetchingOne from './DataFetchingOne';

const App = () => <DataFetchingOne />;


export default App;

2. Run the app using npm start .

3. You should see:

"Loading..." briefly while the data is being fetched.

The post title once the fetch succeeds.

Error message if the API call fails (e.g., incorrect URL).

📌 Key Observations
useState is used to manage multiple states ( loading , error , post ).

State transitions are handled in the then and catch blocks of the Axios
request.

The component dynamically renders content based on the state.

🔜 Next Step
In the following example, we will achieve the same functionality using the
useReducer hook to better manage complex state transitions. 🎥

Code evolution React hook 98


📝 Detailed Notes: When to Use useState vs useReducer 📝
In this video, the presenter explains the scenarios that help determine whether
to use useState or useReducer for state management in React. Both hooks are
used for managing state, but they are better suited for different use cases.

🎯 Key Scenarios for Choosing useState vs useReducer :

1. Type of State Variable


Primitive Types (e.g., number, string, boolean):
Use useState . It's simpler and better suited for handling primitive data types.

Example: Managing a counter ( let count = 0 ).

Complex Types (e.g., objects or arrays):


Use useReducer . It's better for managing complex state like objects or arrays.

Example: Managing a person object with properties like firstName ,


lastName , and age .

2. Number of State Transitions


Few State Transitions (1-2 variables):
Use useState . It's simple and effective for scenarios where you have only a
few state variables to manage.

Code evolution React hook 99


Many State Transitions (5 or more variables):

Use useReducer . If you're updating multiple state variables in response to


several actions, using useReducer helps centralize and manage those
transitions in a predictable manner.

Example: Managing several state variables like loading , error , post , etc.

3. Relation Between State Transitions


Unrelated State Variables:
Use useState if the state variables are independent of each other.

Related State Transitions (e.g., fetching data):

Use useReducer when the state variables are closely related and dependent
on a specific action.

Example: A loading state, error state, and data state are tied together in
a data fetching operation. They should be managed together in the
same reducer for consistency and maintainability.

4. Complexity of State Transitions


Simple State Changes:
Use useState for straightforward updates.

Complex State Changes with Business Logic:


Use useReducer if the transition between states requires complex logic or
data manipulation. The reducer centralizes this logic, making it easier to
manage and debug.

Example: Transforming and updating multiple pieces of state based on


a complex condition.

5. Local vs Global State


Local Component State:
Use useState when managing local state that doesn’t need to be shared
outside the component.

Global State (shared across multiple components):

Code evolution React hook 100


Use useReducer . It works well with the Context API to manage global state,
especially when components deep in the tree need to interact with the
state.

Why useReducer for Global State?

It simplifies passing down state updates. With useReducer , you only


need to pass a single dispatch function down the component tree
instead of multiple setter functions (like with useState ).

Example: Managing global state like user authentication or theme


preferences.

💡 Summary of Best Use Cases:


useState :

Best for simple, local state management, especially for primitive types or
when only a few variables are updated at a time.

useReducer :
Best for managing complex state logic, multiple interdependent state
variables, or global state in larger apps.

🔚 Conclusion:
In general, if you're working with simple state or managing local state, useState
is the go-to. For complex, interrelated states, or when dealing with global state,
useReducer shines.

💡
Transcript Notes for Performance Optimization with
useCallback Hook

import React, { useState } from "react";


import Title from "./Title";
import Count from "./Count";
import Button from "./Button";

function ParentComponent() {
const [age, setAge] = useState(25);
const [salary, setSalary] = useState(50000);

Code evolution React hook 101


const incrementAge = () => setAge(age + 1);
const incrementSalary = () => setSalary(salary + 1000);

return (
<div>
<Title />
<Count label="Age" value={age} />
<Button onClick={incrementAge} text="Increment Age" />
<Count label="Salary" value={salary} />
<Button onClick={incrementSalary} text="Increment Salary
</div>
);
}

export default ParentComponent;

Expected Behavior without useCallback :


When you increment age or salary, all child components (Title, Count, and
Button) will re-render, even if their props haven’t changed.

The incrementAge and incrementSalary functions are recreated every time the
parent re-renders, which causes unnecessary re-renders in the child
components.

Why Does This Happen?


Without useCallback , the functions incrementAge and incrementSalary are recreated
on every render, meaning their reference changes. Even though the logic inside
the functions is identical, React sees them as different functions, so
components like Button and Count are forced to re-render.

🧑‍🏫
Introduction to Performance Optimization & useCallback Hook

React Performance Optimization: When updating a single state, all


components might re-render, which can cause performance issues,
especially in larger applications with many components. We want to ensure
only the necessary components re-render.

Code evolution React hook 102


React's useCallback Hook: Helps in memoizing functions to ensure that they
aren't redefined on every render, thus preventing unnecessary re-renders
in child components.

Code Setup Overview 💻


Parent Component:
The Parent component includes several smaller components:

Title Component: Displays a static title (e.g., "useCallback Hook").

Count Component: Displays either the age or salary.

Button Component: Allows us to increment age or salary by calling


respective functions.

Here is the breakdown of the Parent component:

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


import Title from "./Title";
import Count from "./Count";
import Button from "./Button";

function ParentComponent() {
const [age, setAge] = useState(25);
const [salary, setSalary] = useState(50000);

const incrementAge = () => setAge(age + 1);


const memoizedIncrementAge = useCallback(incrementAge,
[age]);
//or you can also write it

const memoizedIncrementSalary = useCallback(()=>{


setSalary(salary + 1000)}, [salary]);

return (
<div>
<Title />
<Count label="Age" value={age} />
<Button onClick={memoizedIncrementAge} text="Incremen

Code evolution React hook 103


t Age" />
<Count label="Salary" value={salary} />
<Button onClick={memoizedIncrementSalary} text="Incre
ment Salary" />
</div>
);
}

export default ParentComponent;

Key Points:
The Parent component holds the state for age and salary.

is used to memoize the incrementAge and incrementSalary


useCallback

functions, with their respective dependencies ( age and salary ).

Optimization with React.memo 🎯


: A higher-order component that prevents unnecessary re-
React.memo

renders of child components if props haven't changed.

Usage in Child Components:


We wrap the Count and Button components with React.memo to prevent
unnecessary re-renders.

// Title.js
import React from "react";

const Title = () => {


console.log("Rendering Title");
return <h1>useCallback Hook</h1>;
};

export default React.memo(Title);

// Count.js
import React from "react";

Code evolution React hook 104


const Count = ({ label, value }) => {
console.log(`Rendering Count: ${label}`);
return (
<div>
{label}: {value}
</div>
);
};

export default React.memo(Count);

// Button.js
import React from "react";

const Button = ({ onClick, text }) => {


console.log(`Rendering Button: ${text}`);
return <button onClick={onClick}>{text}</button>;
};

export default React.memo(Button);

Why useCallback Matters 🔑


When you update the age or salary, the memoized functions (i.e.,
incrementAge and incrementSalary ) will only be redefined when their

dependencies change.

Without useCallback , each render would create a new function, causing


unnecessary re-renders in child components even when their props haven't
changed.

Without useCallback (Without Optimization) 🚫


Problem: On every state update, new functions are created which break
reference equality, causing unnecessary re-renders in child components.

Example: When you increment the age, the incrementAge function is


recreated on each render, even if it's the same function with the same
behavior.

Code evolution React hook 105


Log Output Without Optimization:

Rendering Title
Rendering Count: Age
Rendering Button: Increment Age
Rendering Count: Salary
Rendering Button: Increment Salary

With useCallback (With Optimization) ✅


Solution: Memoizing the functions ensures that the functions aren't
recreated unless their dependencies change. This leads to fewer re-
renders of child components.

Updated Parent Component with useCallback :

const incrementAge = useCallback(() => setAge(age + 1), [ag


e]);
const incrementSalary = useCallback(() => setSalary(salary
+ 1000), [salary]);

Log Output with Optimization:

Rendering Title
Rendering Count: Age
Rendering Button: Increment Age
Rendering Count: Salary
Rendering Button: Increment Salary

Now, only the relevant components re-render when age or salary changes,
significantly improving performance.

Conclusion 🏁
useCallback optimizes performance by memoizing callback functions and
preventing unnecessary re-renders of child components.

React.memo helps further optimize performance by preventing re-renders of


child components when their props don't change.

Code evolution React hook 106


Both useCallback and React.memo are important tools when working with
functional components and passing functions as props.

Code Snippet Example: Button.js 🎯


import React, { useCallback } from "react";

const Button = React.memo(({ onClick, text }) => {


console.log(`Rendering Button: ${text}`);
return <button onClick={onClick}>{text}</button>;
});

export default Button;

Here: The Button component is optimized with React.memo , and the onClick
function is passed as a prop using useCallback to ensure it doesn't get
redefined on each render.

Best Practices 📚
Don't overuse useCallback . It is only useful when passing functions to
optimized child components, and when re-renders of those components
are costly.

Check dependencies carefully. Always pass the state or props that the
callback depends on as dependencies to ensure the memoized function is
updated correctly.

Let me know if you'd like further details! 🙂


Sure! Here’s a detailed explanation of React.memo :

React.memo – Definition & Usage

What is React.memo ?
is a higher-order component (HOC) that optimizes performance by
React.memo

preventing unnecessary re-renders of functional components. It is used to


memoize the rendered output of a component and only re-renders it if the
props have changed.

Code evolution React hook 107


In simpler terms, React.memo will remember (or memoize) a component’s output,
and only re-render it when the props passed to it have changed.

Why use React.memo ?


By default, React re-renders all components when their parent re-renders. This
can be inefficient when you have large components that don't change often.
Using React.memo helps optimize performance by preventing unnecessary re-
renders for components whose props haven’t changed.

For example, if you have a list of items in a parent component and a list item
component that doesn’t change, wrapping the list item component with
React.memo will ensure it only re-renders if its props change.

How does React.memo work?


Shallow comparison of props: React.memo compares the previous props and
the new props using shallow comparison (checks for equality of primitive
values). If there’s no change in the props, the component is not re-
rendered.

If the props have changed, the component is re-rendered normally.

Usage Example

const MyComponent = React.memo((props) => {


console.log("Rendering MyComponent");
return <div>{props.name}</div>;
});

Without React.memo : The component would re-render every time the parent
re-renders, even if the props haven’t changed.

With React.memo : The component only re-renders if the props.name changes,


not when other props or the parent component state changes.

Syntax

const MyComponent = React.memo(Component);

Code evolution React hook 108


Component : The functional component you want to optimize with React.memo .

React.memo with Custom Comparison


You can provide a custom comparison function to control when the component
should re-render. This function receives the previous props and the next props
as arguments and should return true if the component should not re-render
and false if it should.

const MyComponent = React.memo(Component, (prevProps, nextP


rops) => {
return prevProps.name === nextProps.name;
});

In this example, MyComponent will only re-render if name changes.

When to Use React.memo


Use it when a component renders often but doesn’t need to (e.g., large
lists or static UI components).

Use it for pure components where the output is solely dependent on props.

Avoid using it unnecessarily, as it comes with a slight performance cost due


to the props comparison, which could outweigh the benefits if your
component is small or renders infrequently.

Important Notes
Memoization is based on props: React.memo checks if the props of a
component are different. If props haven’t changed, the component won’t
re-render, even if the parent re-renders.

Not useful for state changes: React.memo only checks the props passed
to a component. State changes inside the component will still cause a re-
render.

For pure components: React.memo is most effective when your component


renders the same output for the same props (i.e., it's a pure component).

Example in Code:

Code evolution React hook 109


Let’s consider the following code where React.memo is used to prevent
unnecessary re-renders:

import React, { useState } from "react";

const Count = React.memo(({ label, value }) => {


console.log(`Rendering Count: ${label}`);
return (
<div>
{label}: {value}
</div>
);
});

function ParentComponent() {
const [age, setAge] = useState(25);
const [salary, setSalary] = useState(50000);

const incrementAge = () => setAge(age + 1);


const incrementSalary = () => setSalary(salary + 1000);

return (
<div>
<h1>Performance Optimization</h1>
<Count label="Age" value={age} />
<button onClick={incrementAge}>Increment Age</button>
<Count label="Salary" value={salary} />
<button onClick={incrementSalary}>Increment Salary</b
utton>
</div>
);
}

export default ParentComponent;

In this example:

Count component is wrapped with React.memo .

Code evolution React hook 110


When age or salary changes, only the corresponding Count component will
re-render, and the other will be prevented from re-rendering.

This improves the performance of the application, especially if you have


many such components.

Summary:
helps to optimize performance by preventing unnecessary re-
React.memo

renders of functional components.

It performs a shallow comparison of props to determine if the component


should re-render.

It’s most useful in pure components where the output depends entirely on
props.

Use it carefully; memoization can have overhead and isn’t always


beneficial for small or rarely re-rendered components.

Takeaway for Notes:


React.memo is a performance optimization technique used to prevent
unnecessary re-renders in functional components. It works by memoizing the
component’s output and only re-rendering the component if its props have
changed. You can pass a custom comparison function to control when re-
renders happen.
This is especially useful in large applications or components that render
frequently, like lists or static UI elements. However, it should be used
selectively as unnecessary memoization might incur a small performance cost.

Transcript: Understanding useMemo for Performance


Optimization

🎥 In this video, we dive into another React hook designed for performance
optimization: useMemo .

Key Topics Covered:

Code evolution React hook 111


Introduction to useMemo

Setting up a Performance Example

Performance Issues Without useMemo

Using useMemo to Optimize Performance

Key Differences Between useMemo and useCallback

1. Introduction to useMemo
In the previous video, we explored the useCallback hook, which optimizes
performance by preventing unnecessary re-renders of functions. Today, we’re
going to look at useMemo , which performs a similar job but focuses on
memoizing computed values instead of functions.

2. Setting Up a Performance Example


We start by creating a new file counter.js and implement a simple React
component using useState for two counters.

Initial Setup (Without useMemo )

import React, { useState } from "react";

const Counter = () => {


const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);

const increment1 = () => setCounter1(counter1 + 1);


const increment2 = () => setCounter2(counter2 + 1);

const isEven = () => {


let i = 0;
while (i < 1000000000) {
i++; // Simulate delay
}
return counter1 % 2 === 0;
};
return (
<div>

Code evolution React hook 112


<div>
<button onClick={increment1}>Count 1: {counter1}</b
utton>
<span>{isEven() ? "Even" : "Odd"}</span>
</div>
<div>
<button onClick={increment2}>Count 2: {counter2}</b
utton>
</div>
</div>
);
};

export default Counter;

3. Performance Issues Without useMemo

The Problem:
When you click to increment counter1, the function isEven() calculates whether
the number is odd or even, which works fine initially. However, clicking on
counter2 also slows down the UI.

Why Does This Happen?


Reason: Every time the state changes (e.g., when counter2 is updated),
React re-renders the component, and during this re-render, the isEven()
function is recalculated.

Result: Even though counter2 doesn’t affect the result of isEven() , React
recalculates it, causing delays in UI updates.

4. Using useMemo to Optimize Performance


Now, we introduce useMemo to memoize the result of isEven() and prevent
unnecessary recalculations when the value of counter1 doesn’t change.

Implementation of useMemo

Code evolution React hook 113


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

const Counter = () => {


const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);

const increment1 = () => setCounter1(counter1 + 1);


const increment2 = () => setCounter2(counter2 + 1);

// Use useMemo to cache the result of isEven calculation


const isEven = useMemo(() => {
let i = 0;
while (i < 1000000000) { // Introduce delay
i++;
}
return counter1 % 2 === 0;
}, [counter1]); // Recalculate only when counter1 changes
return (
<div>
<div>
<button onClick={increment1}>Count 1: {counter1}</b
utton>
<span>{isEven ? "Even" : "Odd"}</span>
</div>
<div>
<button onClick={increment2}>Count 2: {counter2}</b
utton>
</div>
</div>
);
};

export default Counter;

How useMemo Works:

Code evolution React hook 114


First Argument: The function that calculates the value you want to
memoize ( counter1 % 2 === 0 ).

Second Argument: The dependency array. In this case, useMemo will only
recompute the result when counter1 changes. If counter1 doesn’t change,
the previously cached value is used.

Behavior After useMemo :


When you click counter1, the UI updates, and the odd/even calculation is
re-evaluated.

When you click counter2, React uses the cached value of isEven without
recalculating, speeding up the UI updates.

5. Key Differences Between useMemo and useCallback


useMemo :

Caches the result of a function call (its output).

Best used when you want to memoize expensive computations or


values.

Example use case: Avoid recalculating whether a number is even/odd


when it hasn’t changed.

useCallback :

Caches the function instance itself.

Best used when you want to prevent recreation of the function during
every render.

Example use case: Prevent re-creation of event handlers like increment1

or increment2 .

6. Conclusion
useMemo helps optimize performance by caching computed values and
recalculating them only when dependencies change.

Use useMemo when you need to memoize calculations or expensive


operations that shouldn’t run on every render.

useCallback is similar but caches functions instead of their results.

Code evolution React hook 115


Takeaways:
When to use useMemo :

Use useMemo for memoizing expensive calculations like filtering or


sorting large arrays, complex calculations, etc.

It’s especially beneficial when computations depend on props or state


that don’t change often.

When to use useCallback :

Use useCallback when you need to prevent re-creation of functions


passed to child components (like event handlers).

Final Thoughts:
Performance Optimization with Hooks in React helps you ensure that your
components render efficiently, especially when working with complex or
resource-heavy logic. Using hooks like useMemo and useCallback properly can
significantly improve the performance of your application.

React useRef Hook: Detailed Notes with Code Example


In this video, we discussed the useRef hook in React, which is used to directly
access DOM nodes and hold mutable values that do not trigger re-renders. It's
commonly used for scenarios like focusing an input field, managing timers, or
accessing a DOM element outside the React render cycle.
Here's a detailed breakdown of what was discussed:

What is useRef ?
allows us to persist values across renders without causing re-
useRef

renders.

It can hold references to DOM elements, meaning it gives us direct access


to a specific element in the DOM (such as an input field).

Common Use Case: Focusing an Input on Page Load


We’ll implement a login form where, upon page load, the username input field
is automatically focused using the useRef hook.

Code evolution React hook 116


Steps to Achieve Focus on Page Load Using useRef

1. Setup
Create a new file called FocusInput.js .

Use RFC (React Functional Component) snippet to quickly generate the


basic functional component.

2. Create the JSX for Input

import React from 'react';

const FocusInput = () => {


return (
<input type="text" />
);
};

export default FocusInput;

At this point, we have a basic input field, but no logic to focus it when the page
loads.

3. Implementing the Focus Behavior


We want to focus on the input only once, after the component mounts. For this,
we can use the useEffect hook.

import React, { useEffect, useRef } from 'react';

const FocusInput = () => {


const inputRef = useRef(null); // Step 1: Create a refer
ence

useEffect(() => {
inputRef.current.focus(); // Step 3: Focus the input f
ield
}, []); // Empty dependency array ensures this effect run
s only once, when the component mounts

Code evolution React hook 117


return (
<input type="text" ref={inputRef} /> // Step 2: Attach
the ref to the input element
);
};

export default FocusInput;

Code Explanation
Step 1:

const inputRef = useRef(null); creates a reference to the input element.


Initially, inputRef.current is null , but once the component mounts, it will
point to the actual DOM node.

Step 2:

We attach the inputRef to the input element using the ref={inputRef}

attribute. This links the inputRef variable to the DOM element, so we can
access it directly.

Step 3:

In the useEffect hook, inputRef.current.focus(); is used to focus the input


field once the component is mounted. The current property holds the
DOM node, which can be accessed to call methods like focus() .

Why Use useRef ?


Direct DOM Access: useRef provides a way to access the DOM directly.
This can be particularly useful when you need to interact with a DOM
element in ways that React's declarative approach doesn't easily handle
(like focusing an element).

Avoiding Re-renders: Unlike using state, useRef doesn't trigger a re-render


when the value changes. This makes it efficient for scenarios where you
don't need React to re-render the component when interacting with the
DOM.

Additional Use Cases for useRef

Code evolution React hook 118


Storing Mutable Values: You can store values that you don’t want to trigger
a re-render when updated. For example, storing a previous value or
tracking the number of times a function has been called.

Accessing DOM Elements for Interactions: Apart from focusing input


fields, you might need to access a DOM element for other purposes, like
measuring its dimensions, scrolling, or managing animations.

Summary
allows direct manipulation of DOM nodes in functional components
useRef

without causing re-renders.

Used commonly for tasks like focusing an input on mount or storing


mutable values.

Works seamlessly with useEffect for DOM interactions after the component
has mounted.

This is just one of the many use cases of useRef . We'll explore more in-depth
examples in future videos.

React Native Example of useRef


In React Native, the useRef hook is commonly used for tasks like focusing input
fields, managing animations, and directly interacting with components. Here's a
simple example where we use useRef to focus on a TextInput component when
the page loads.

Scenario:
We will create a simple form with a TextInput for the user to enter their
username. Upon loading the page, we want the TextInput to automatically be
focused.

Step-by-Step Example
1. Set up the Project:
If you haven't set up a React Native project yet, run the following command
in your terminal:

Code evolution React hook 119


npx react-native init UseRefExample
cd UseRefExample

2. Install Dependencies:
Make sure to have React Native set up correctly on your system with all
dependencies.

3. Create the Component:


In the
App.js file (or any custom component), we'll write the following code to

implement the useRef hook.

App.js (React Native Example with useRef )

import React, { useEffect, useRef } from 'react';


import { View, TextInput, Text, Button, StyleSheet } from
'react-native';

const App = () => {


// Step 1: Create a reference using useRef hook
const inputRef = useRef(null);

// Step 2: Use the ref to focus the TextInput after the c


omponent mounts
useEffect(() => {
// Focus the TextInput on component mount
inputRef.current?.focus();
}, []);

return (
<View style={styles.container}>
<Text style={styles.title}>Focus on the input field o
n load</Text>

{/* Step 3: Attach the ref to the TextInput */}


<TextInput
ref={inputRef} // Attach the useRef to TextInput
style={styles.input}

Code evolution React hook 120


placeholder="Enter username"
/>

<Button title="Submit" onPress={() => alert('Form Sub


mitted!')} />
</View>
);
};

// Styles
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
title: {
fontSize: 20,
marginBottom: 20,
},
input: {
height: 40,
width: '80%',
borderColor: 'gray',
borderWidth: 1,
marginBottom: 20,
paddingHorizontal: 10,
},
});

export default App;

Explanation of Code:
Step 1:

creates a reference to the TextInput


const inputRef = useRef(null);

component. Initially, inputRef.current is null , but when the component

Code evolution React hook 121


mounts, React will assign the actual DOM node of the TextInput to
inputRef.current .

Step 2:

Inside the hook, we check if inputRef.current exists and call


useEffect

inputRef.current?.focus() . This focuses the TextInput when the

component is mounted.

Step 3:

In the JSX, we attach the inputRef to the TextInput using the ref=

attribute. This connects the reference variable to the actual


{inputRef}

component.

Key Points:
useRefis used to access and manipulate the DOM elements or component
instances directly.

In this example, inputRef.current?.focus() focuses the TextInput as soon as


the component mounts.

Using useRef prevents unnecessary re-renders since the focus operation


doesn’t trigger a state update.

Result:
When you run this React Native app, the TextInput will automatically receive
focus when the component is loaded, without any need for additional user
interaction.

This is a basic example showing how useRef works in React Native. You can
extend this pattern to handle animations, scroll-to components, or managing
multiple refs for complex layouts.

The reason for the difference between current.focus() in React JS and


current!.focus() in React Native comes down to TypeScript's nullability and

strictness in handling references.

In React (JS/TS):

Code evolution React hook 122


In React (Web/JSX), when using useRef to reference a DOM element, current
may be null initially, as React hasn't attached the DOM element to the
reference yet (on the first render). However, once the component has mounted,
React assigns the DOM element to the ref .

In TypeScript, React typically has the following type for the useRef hook:

const inputRef = useRef<HTMLInputElement | null>(null);

This means inputRef.current can either be null (before mounting) or an


HTMLInputElement (after mounting).

TypeScript Warning (Without ! Operator):


TypeScript is strict about potential null values and will warn you if you're
trying to access a property (like focus() ) on a potentially null value, as
inputRef.current could be null before mounting.

Without the non-null assertion operator ( ! ), TypeScript would flag an error:

inputRef.current.focus(); // Error: Object is possibly 'nu


ll'

With Non-Null Assertion ( ! ):


The non-null assertion operator ( ! ) tells TypeScript that you are confident the
value will never be null at this point in the code, so it won't show a warning.

inputRef.current!.focus(); // No error

In this case, you are asserting that inputRef.current will always be a valid
HTMLInputElement at the point when focus() is called (i.e., after the component
has mounted).

In React Native:
In React Native, the same concept applies, but TypeScript's nullability and the
DOM abstraction might make you more comfortable asserting that the current
property won't be null in certain scenarios, especially since you often use
useRef for managing components like TextInput , which are guaranteed to be

available after mounting.

Code evolution React hook 123


Thus, when accessing focus() in React Native, we often use:

inputRef.current!.focus();

Here, the ! indicates to TypeScript that the ref will definitely be assigned the
DOM element (e.g., TextInput ), and there’s no need to check for null .

Summary of Differences:
React (Web): Since you are directly interacting with HTML DOM elements,
TypeScript needs to ensure that current is never null before you call
methods like focus() . Without the ! operator, TypeScript flags potential
issues where current could be null .

React Native: React Native doesn't work directly with the DOM but still
manages components similarly. The ! is commonly used to bypass
nullability checks in TypeScript, asserting that after the component mounts,
the current reference will be valid.

Best Practice:
While the ! operator works fine when you are confident about the element’s
existence, it’s good practice to add null checks or guards (like optional chaining
?. ) to ensure safety, especially if the component is not guaranteed to always

render as expected.

Safe Approach:

inputRef.current?.focus(); // Safe, avoids errors if curre


nt is null

This approach is typically preferred over using ! because it ensures that the
code won't crash if current is unexpectedly null .

Yes, you can use both the non-null assertion operator ( ! ) and optional
chaining ( ?. ), but each has a different use case and trade-offs:

1. Non-null Assertion ( ! ):

Code evolution React hook 124


The non-null assertion operator ( ! ) tells TypeScript that you are certain that
the value will never be null or undefined at that point in the code. It's a way to
bypass TypeScript's nullability checks.

When to use: You should use ! when you are confident that the reference
( current ) will not be null or undefined at that point, often because you know
the component is already mounted.

Example:

inputRef.current!.focus(); // Tells TypeScript that inp


utRef.current is definitely not null

Caution: Overusing ! can lead to runtime errors if your assumption about


the reference being non-null is incorrect.

2. Optional Chaining ( ?. ):
Optional chaining ( ?. ) is safer and ensures that you only attempt to access
properties (like focus() ) if the object (like inputRef.current ) is not null or
undefined .

When to use: Use ?. when you want to safely handle cases where the
value might be null or undefined (for example, when the component hasn't
mounted yet).

Example:

inputRef.current?.focus(); // If current is null or und


efined, focus() won't be called

Benefit: This ensures your code won’t throw errors if the reference is null

at any point.

Which One to Use?:


Use ! (Non-null Assertion) when you are absolutely sure the reference
will never be null when accessed. This is typical in lifecycle methods
where the ref is guaranteed to be set after the component has mounted.

Use ?. (Optional Chaining) when you want to safely access the property,
especially when there is a chance that the ref could be null or undefined at

Code evolution React hook 125


some point in the component's lifecycle.

Best Practice:
Safe approach (Recommended): Use optional chaining ( ?. ) as it's safer
and avoids potential runtime crashes if the reference is not ready or the
component is unmounted.

inputRef.current?.focus();

Use ! cautiously: Only use non-null assertion ( ! ) when you are sure the
value will always be available (e.g., after component mounts or in specific
lifecycle scenarios).

inputRef.current!.focus();

This makes your code more robust and less prone to errors, especially when
dealing with asynchronous rendering or conditions where refs might not be
immediately available.

UseRef Hook in React: Key Takeaways

1. Introduction to useRef Hook:


The useRef hook allows us to access and manipulate DOM elements directly in
React functional components. It is often used for storing mutable values that
persist across renders without causing re-renders when updated.

2. Typical Use Cases for useRef :


Accessing DOM nodes (like focusing an input field).

Storing mutable variables (like an interval timer) that do not need to trigger
re-renders when changed.

Code evolution React hook 126


Example: Using useRef for an Interval Timer in React
Functional Components

Step-by-Step:
1. Class Component with Timer (Before useRef ):

Set up an interval timer that updates a state variable every second.

Used class component lifecycle methods ( componentDidMount ,


componentWillUnmount ) for setting up and clearing the timer.

// Class Component: Timer


class Timer extends React.Component {
constructor() {
super();
this.state = { timer: 0 };
this.interval = null; // Store the interval ID
}

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

componentWillUnmount() {
clearInterval(this.interval); // Clean up on unmount
}

render() {
return (
<div>
Timer: {this.state.timer}
<button onClick={() => clearInterval(this.interva
l)}>Clear Timer</button>
</div>
);

Code evolution React hook 127


}
}

1. Functional Component with useEffect :

Convert the class component to a functional component using useState

and useEffect .

The timer setup and cleanup are handled in the useEffect hook.

// Functional Component with useEffect


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

const HookTimer = () => {


const [timer, setTimer] = useState(0);

useEffect(() => {
const interval = setInterval(() => {
setTimer(prev => prev + 1);
}, 1000);

return () => clearInterval(interval); // Cleanup on un


mount
}, []); // Empty dependency array to run only once

return (
<div>
Timer: {timer}
<button onClick={() => clearInterval(interval)}>Clear
Timer</button>
</div>
);
}

1. Problem: Accessing Interval ID in Event Handler:

When trying to clear the interval on a button click, an error occurs:


interval is not defined because it's scoped inside the useEffect hook.

Solution: Using useRef to Store Mutable Value

Code evolution React hook 128


To fix this, we store the interval ID using useRef so that it persists between
renders and can be accessed anywhere within the component without causing
re-renders.

How to Use useRef :

Create a useRef variable to store the interval ID.

useRefdoes not trigger re-renders when its value is updated, making it


perfect for storing mutable values like the interval ID.

// Functional Component with useRef for Timer


import React, { useState, useEffect, useRef } from 'react';

const HookTimer = () => {


const [timer, setTimer] = useState(0);
const intervalRef = useRef(null); // Store the interval
ID

useEffect(() => {
intervalRef.current = setInterval(() => {
setTimer(prev => prev + 1);
}, 1000);

return () => clearInterval(intervalRef.current); // Cl


eanup on unmount
}, []); // Empty dependency array to run only once

const clearTimer = () => {


clearInterval(intervalRef.current); // Access interval
ID from useRef
}

return (
<div>
Timer: {timer}
<button onClick={clearTimer}>Clear Timer</button>
</div>

Code evolution React hook 129


);
}

Key Benefits of useRef Here:


Stores mutable values: The interval ID is stored in intervalRef.current , which
persists across renders.

Does not trigger re-renders: Updating intervalRef does not cause re-
renders, unlike state variables.

Useful for handling mutable objects: Any value that needs to be mutable
but does not affect the render cycle can be stored in useRef .

Summary of Key Points:


useRef Basics:

Provides access to DOM elements and mutable values.

Values stored in useRef persist across renders but do not cause re-
renders when updated.

Common Use Cases:

Storing DOM references for focus, scroll position, etc.

Holding mutable values that are needed across renders (like interval
IDs, previous state, etc.).

Example: Using useRef to store an interval ID and clear the timer without
causing re-renders.

This example highlights how the useRef hook can be effectively used for
scenarios where we need to store a value (like an interval ID) without affecting
the component's render cycle. This is particularly useful when dealing with
side-effects that don't need to trigger re-renders, such as clearing a timer.

Creating Custom Hooks in React

1. Introduction to Custom Hooks:

Code evolution React hook 130


Custom hooks in React allow us to extract component logic into reusable
functions. They are just JavaScript functions, but their names must start with
. Custom hooks can also call built-in hooks like useState , useEffect , and
use

others, enabling us to encapsulate logic that can be shared across


components.

Why Use Custom Hooks?


Reusability: Share common logic across multiple components without
duplication.

Simplicity: A cleaner and more maintainable approach compared to higher-


order components (HOCs) or render props.

Composability: Custom hooks can be composed together to create more


complex logic.

How to Create a Custom Hook:


A custom hook is simply a JavaScript function.

The function's name must start with use (this is a React convention to
identify hooks).

It can call other hooks internally to manage state, side effects, etc.

The goal is to abstract logic that could be reused by multiple components.

Step-by-Step Example: Creating a Custom Hook for Form


Handling
In this example, we'll create a custom hook to handle form state in a React
functional component.

1. Create the useForm Custom Hook:


This custom hook will manage form state and provide methods to update the
form, reset it, and get form values.

// useForm.js (Custom Hook)


import { useState } from 'react';

// Custom Hook: useForm


const useForm = (initialState) => {

Code evolution React hook 131


const [formData, setFormData] = useState(initialState);

// Update form value


const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevState) => ({
...prevState,
[name]: value,
}));
};

// Reset form to initial state


const resetForm = () => {
setFormData(initialState);
};

return {
formData,
handleChange,
resetForm,
};
};

export default useForm;

2. Use the useForm Custom Hook in a Component:


Now, we'll use this custom hook in a form component to handle input changes
and form resets.

// FormComponent.js
import React from 'react';
import useForm from './useForm';

const FormComponent = () => {


// Initialize form state with useForm custom hook
const { formData, handleChange, resetForm } = useForm({
name: '',

Code evolution React hook 132


email: '',
});

const handleSubmit = (event) => {


event.preventDefault();
console.log('Form submitted:', formData);
};

return (
<div>
<h2>Form with Custom Hook</h2>
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
<button onClick={resetForm}>Reset Form</button>
</div>
);
};

export default FormComponent;

Explanation of the Code:


useForm Custom Hook:

Code evolution React hook 133


Takes an initialState as an argument (default values for the form).

Manages form data with useState .

Provides handleChange to update the form values on input change.

Provides resetForm to reset the form data to its initial state.

FormComponent :

Uses the useForm custom hook to manage the form state.

Calls handleChange on input changes and resetForm when the "Reset


Form" button is clicked.

Submits the form and logs the form data when the form is submitted.

Key Benefits:
Reusability: The useForm hook can be reused in multiple forms across your
application.

Clean Code: The form logic is encapsulated in the custom hook, making the
component code cleaner and easier to manage.

Separation of Concerns: The form state management is separate from the


UI code.

Key Points to Remember:


Naming Convention: Custom hook names should start with use (e.g.,
useForm , useTimer ).

Calling Other Hooks: Custom hooks can call React’s built-in hooks, such as
useState , useEffect , useReducer , etc.

Return Values: Custom hooks usually return an object or array containing


the values and functions that need to be used by the components.

Summary:
Custom hooks in React are a powerful way to share logic between components.
By creating a function that starts with use , you can encapsulate reusable
functionality and simplify your code. In this example, we created a custom
hook, useForm , to manage form states, which made our form components

Code evolution React hook 134


cleaner and more maintainable. Custom hooks promote reusability,
modularity, and better separation of concerns.

Creating Our First Custom Hook in React: A Step-by-Step


Guide

1. Introduction:
In this video, we’re creating our first custom hook. To keep it simple, the
custom hook will update the document title based on a counter. In the first half,
we'll implement the logic directly in the component without using a custom
hook. In the second half, we’ll refactor the code to extract the logic into a
reusable custom hook.

2. Implementation Without Custom Hook:


Component Creation: We start by creating a simple functional component,
which contains a counter.

State & Effect:

We use useState to manage the counter's state.

We use useEffect to update the document title whenever the counter


value changes.

Code Without Custom Hook:

// docTitle1.js
import React, { useState, useEffect } from 'react';

const DocTitle1 = () => {


const [count, setCount] = useState(0);

// Update document title when count changes


useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Dependency on count

Code evolution React hook 135


return (
<div>
<button onClick={() => setCount(count + 1)}>Count: {c
ount}</button>
</div>
);
};

export default DocTitle1;

Explanation:

State ( count ): Tracks the counter value.

Effect ( useEffect ): Updates the document title whenever the count value
changes.

Button: Increments the counter value when clicked.

3. Replicating Logic in Another Component:


To simulate repeating the logic, let’s create a second component that does the
same thing.

// docTitle2.js
import React, { useState, useEffect } from 'react';

const DocTitle2 = () => {


const [count, setCount] = useState(0);

// Update document title when count changes


useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);

return (
<div>
<button onClick={() => setCount(count + 1)}>Count: {c
ount}</button>
</div>

Code evolution React hook 136


);
};

export default DocTitle2;

Problem: Both components are repeating the same logic to update the
document title. This duplication isn’t scalable when you have many
components requiring the same behavior.

4. Extracting Logic into a Custom Hook:


We will now create a custom hook to extract the logic of updating the
document title, which can be reused in both components.

Create useDocumentTitle Hook:

The hook will take a count value and update the document title.

It uses useEffect internally to manage side effects.

Code for Custom Hook ( useDocumentTitle.js ):

// useDocumentTitle.js
import { useEffect } from 'react';

// Custom hook to update document title


const useDocumentTitle = (count) => {
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Dependency on count
};

export default useDocumentTitle;

Explanation:

The custom hook useDocumentTitle accepts a count parameter.

It uses useEffect to update the document title whenever count changes.

5. Refactoring Components to Use Custom Hook:

Code evolution React hook 137


Now, we will refactor the two components to use the useDocumentTitle hook
instead of directly implementing the useEffect .

Refactored Component 1:

// docTitle1.js
import React, { useState } from 'react';
import useDocumentTitle from './useDocumentTitle';

const DocTitle1 = () => {


const [count, setCount] = useState(0);

// Use the custom hook to update document title


useDocumentTitle(count);

return (
<div>
<button onClick={() => setCount(count + 1)}>Count: {c
ount}</button>
</div>
);
};

export default DocTitle1;

Refactored Component 2:

// docTitle2.js
import React, { useState } from 'react';
import useDocumentTitle from './useDocumentTitle';

const DocTitle2 = () => {


const [count, setCount] = useState(0);

// Use the custom hook to update document title


useDocumentTitle(count);

return (

Code evolution React hook 138


<div>
<button onClick={() => setCount(count + 1)}>Count: {c
ount}</button>
</div>
);
};

export default DocTitle2;

Explanation:

Both DocTitle1 and DocTitle2 components now use the useDocumentTitle

custom hook.

This eliminates the need to repeat the document title update logic in
each component.

6. Advantages of Using Custom Hook:


Code Reusability: We avoid duplicating logic. The custom hook
useDocumentTitle can be used in any component.

Cleaner Code: Components are simplified, focusing only on rendering the


UI.

Maintainability: When we need to change how the document title is set, we


only need to update the custom hook, not every component.

7. Code Walkthrough:
DocTitle1 and DocTitle2 Components:

Both components have a counter ( count ), and the document title


reflects this count.

They use the useDocumentTitle hook to update the document title


whenever the count changes.

useDocumentTitle Custom Hook:

It encapsulates the logic for updating the document title. It listens to


changes in the count value and updates the document title accordingly.

8. Final Notes:

Code evolution React hook 139


This example demonstrates a simple use case of creating a custom hook.

Custom hooks allow us to abstract reusable logic into a single function and
share it across multiple components.

While this example may seem trivial, custom hooks become much more
powerful as your applications grow and the logic you need to reuse
becomes more complex.

Summary:
We started with two components that duplicated logic for updating the
document title.

We then created a custom hook ( useDocumentTitle ) to handle the document


title update, making the code reusable.

Finally, we refactored both components to use the custom hook, simplifying


the code and enhancing maintainability. Custom hooks are a powerful
feature in React for reusability and cleaner code.

Creating a Custom Counter Hook in React: A Detailed Guide

🔹 1. Introduction:
In this video, we are going to create a custom hook called useCounter that will
allow us to reuse the logic for a simple counter. We'll break this into two parts:

1. Without custom hook: Implement a counter in a regular React component.

2. With custom hook: Refactor the logic into a reusable custom hook.

🔹 2. Implementing Counter Without Custom Hook:


First, we’ll build a simple counter with three functionalities:

Increment the count.

Decrement the count.

Reset the count.

Code Without Custom Hook:

Code evolution React hook 140


// counter1.js
import React, { useState } from 'react';

const Counter1 = () => {


const [count, setCount] = useState(0);

// Define increment, decrement, and reset functions


const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};

export default Counter1;

Explanation:

State ( count ): Manages the counter value.

Functions ( increment , decrement , reset ): Handle the three counter


operations.

JSX: Displays the counter value and buttons to interact with it.

Code for Second Counter Component (simulating repeated


logic):

// counter2.js
import React, { useState } from 'react';

const Counter2 = () => {


const [count, setCount] = useState(0);

Code evolution React hook 141


const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(0);

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};

export default Counter2;

Problem: The code is duplicated in both Counter1 and Counter2


components. This isn't scalable when you need the same functionality in
multiple places.

🔹 3. Extracting Logic into a Custom Hook ( useCounter ):


To avoid duplication, we’ll create a custom hook called useCounter . This custom
hook will encapsulate the counter logic, making it reusable across different
components.

Creating the useCounter Custom Hook:

// useCounter.js
import { useState } from 'react';

// Custom hook to handle counter logic


const useCounter = (initialCount = 0, value = 1) => {
const [count, setCount] = useState(initialCount);

const increment = () => setCount(count + value);


const decrement = () => setCount(count - value);
const reset = () => setCount(initialCount);

Code evolution React hook 142


return [count, increment, decrement, reset];
};

export default useCounter;

Explanation:

useCounter Hook: Accepts two optional parameters:

initialCount : The starting value of the counter (defaults to 0 ).

value : The step value by which the counter increments/decrements


(defaults to 1 ).

It returns an array with:

count : The current counter value.

increment : Function to increase the count.

decrement : Function to decrease the count.

reset : Function to reset the count to the initial value.

🔹 4. Refactoring Components to Use the Custom Hook:


Now, we will refactor the Counter1 and Counter2 components to use the
useCounter custom hook instead of manually managing the state and functions.

Refactored Counter1 Component:

// counter1.js
import React from 'react';
import useCounter from './useCounter';

const Counter1 = () => {


const [count, increment, decrement, reset] = useCounter
(); // Using the custom hook

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>

Code evolution React hook 143


<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};

export default Counter1;

Refactored Counter2 Component (with different initial count):

// counter2.js
import React from 'react';
import useCounter from './useCounter';

const Counter2 = () => {


const [count, increment, decrement, reset] = useCounter(1
0); // Initial count set to 10

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};

export default Counter2;

Explanation:

Both components now use the custom hook useCounter to manage their
counter logic.

Counter1 uses the default initial count ( 0 ).

Counter2 starts with a count of 10 .

🔹 5. Customizing the Step Value in the Hook:


Code evolution React hook 144
We can further improve the useCounter hook by allowing different step values for
the increment/decrement operations.

Updated useCounter Hook to Handle Step Value:

// useCounter.js
import { useState } from 'react';

const useCounter = (initialCount = 0, value = 1) => {


const [count, setCount] = useState(initialCount);

const increment = () => setCount(count + value);


const decrement = () => setCount(count - value);
const reset = () => setCount(initialCount);

return [count, increment, decrement, reset];


};

export default useCounter;

Using Custom Step Value in Counter1 and Counter2 :

// counter1.js (default step value is 1)


import React from 'react';
import useCounter from './useCounter';

const Counter1 = () => {


const [count, increment, decrement, reset] = useCounter
(); // Step value = 1

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);

Code evolution React hook 145


};

export default Counter1;

// counter2.js (step value is 10)


import React from 'react';
import useCounter from './useCounter';

const Counter2 = () => {


const [count, increment, decrement, reset] = useCounter(1
0, 10); // Step value = 10

return (
<div>
<h2>{count}</h2>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
<button onClick={reset}>Reset</button>
</div>
);
};

export default Counter2;

Explanation:

In Counter1 , the step value remains 1 .

In Counter2 , the step value is 10 , meaning each increment/decrement


operation will change the count by 10.

🔹 6. Final Notes:
Custom Hooks: Allow you to extract reusable logic for managing state and
side effects, making your components cleaner and more maintainable.

Flexibility: We enhanced the useCounter hook to allow for dynamic initial


count and step value, demonstrating how hooks can be easily customized.

Summary:

Code evolution React hook 146


We started by building a counter in two components.

We refactored the logic into a custom hook ( useCounter ), eliminating code


duplication.

We added features to the custom hook to allow custom initial values and
step values.

Custom hooks are incredibly useful for managing reusable logic and can be
customized to meet various needs.

🚀 Building a Custom Hook for Input Elements in React


1. Introduction:
In this video, we're diving into input elements and building a custom hook to
encapsulate the logic of controlled components. We'll break it down into two
parts:

1. Without custom hook: Implement a basic form with first and last name
input fields.

2. With custom hook: Move the input logic into a custom hook for better code
reuse.

2. Step 1: Building a Form Without Custom Hook


We start by creating a simple user form where the user enters their first name
and last name, and we display an alert upon submission.

Code:

// userForm.js
import React, { useState } from 'react';

const UserForm = () => {


const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');

const handleSubmit = (e) => {


e.preventDefault();

Code evolution React hook 147


alert(`Hello ${firstName} ${lastName}`);
};

return (
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<input
type="text"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
</div>
<div>
<label>Last Name</label>
<input
type="text"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
</div>
<button type="submit">Submit</button>
</form>
);
};

export default UserForm;

🔹 Breakdown:
State management: Two useState hooks for firstName and lastName .

Controlled Components: Each input is controlled by its respective state.

Submit handler: Displays a greeting message in an alert when the form is


submitted.

Test Results:

Code evolution React hook 148


When the user enters "Bruce Wayne" and clicks Submit, the alert displays:
Hello Bruce Wayne .

3. Step 2: Creating a Custom Hook ( useInput )


Next, we create a custom hook called useInput to encapsulate the logic of
binding the input field's value and handling the onChange event.

Creating useInput Hook:

// useInput.js
import { useState } from 'react';

const useInput = (initialValue = '') => {


const [value, setValue] = useState(initialValue);

const reset = () => setValue(initialValue);

const bind = {
value,
onChange: (e) => setValue(e.target.value),
};

return [value, bind, reset];


};

export default useInput;

🔹 Breakdown:
State: A useState hook manages the value of the input.

Reset: A reset function to reset the input value to the initial value.

Binding: The bind object provides value and onChange to connect the input
field.

What It Returns:
1. value: The current value of the input field.

Code evolution React hook 149


2. bind: An object with value and onChange properties to bind to the input field.

3. reset: A function to reset the input field.

4. Step 3: Refactoring the Form to Use the Custom Hook


Now we refactor the UserForm to use our new useInput custom hook.

Updated UserForm Using useInput :

// userForm.js
import React from 'react';
import useInput from './useInput';

const UserForm = () => {


const [firstName, bindFirstName, resetFirstName] = useInp
ut('');
const [lastName, bindLastName, resetLastName] = useInput
('');

const handleSubmit = (e) => {


e.preventDefault();
alert(`Hello ${firstName} ${lastName}`);
resetFirstName();
resetLastName();
};

return (
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<input {...bindFirstName} />
</div>
<div>
<label>Last Name</label>
<input {...bindLastName} />
</div>
<button type="submit">Submit</button>
</form>

Code evolution React hook 150


);
};

export default UserForm;

🔹 Breakdown:
Using useInput : We call useInput for both firstName and lastName .

Destructuring: We extract value , bind , and reset from the hook.

Spreading bind : We use the spread operator ( {...bindFirstName} ) to bind the


value and onChange to the input fields.

Reset functionality: After submission, we reset the values using


resetFirstName() and resetLastName() .

Test Results:
The form works exactly the same as before, but now with less boilerplate
code. The custom hook manages the state and logic, making the form more
maintainable.

5. Why Custom Hooks Rock 🌟:


Custom hooks are fantastic because they allow us to reuse logic in a clean and
manageable way. In this example, we moved the repetitive logic of handling
input fields into a reusable useInput hook. This keeps our components
drastically simpler and helps keep the logic decoupled.

🔹 Endless Possibilities with Custom Hooks!


You can create custom hooks for form validation, fetching data, animations,
and so much more.

There are tons of pre-built hooks out there, but building your own helps
you understand the magic of React hooks!

6. Conclusion 🏁:
We built a simple user form with controlled components, then refactored it
using a custom hook ( useInput ). Here's what we achieved:

Cleaner code by moving logic into reusable hooks.

Code evolution React hook 151


Reset functionality.

More maintainable and readable code.

Key Takeaway: Custom hooks make managing logic much easier, leading to
cleaner and more scalable React apps! 👏

🌟 React Native Custom Hook Example


Custom hooks in React Native allow you to encapsulate reusable logic, making
your components cleaner and more modular. Below, we'll create a custom hook
for managing network requests (e.g., fetching data from an API).

Problem:
Let's say we want to fetch data from a server (e.g., a list of users) and handle
the loading, error, and success states. This logic might be reused across
multiple components, so we create a custom hook for it.

Step-by-Step Guide:

1. Creating the Custom Hook - useFetch.js


This custom hook will handle the fetching of data from an API.

// useFetch.js
import { useState, useEffect } from 'react';
import { Alert } from 'react-native';

const useFetch = (url) => {


// States to store the data, loading status, and errors
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

// Fetch data when the component mounts or URL changes


useEffect(() => {
const fetchData = async () => {
try {

Code evolution React hook 152


setLoading(true); // Start loading
const response = await fetch(url); // Fetch data fr
om API
const result = await response.json(); // Parse the
response as JSON
setData(result); // Set the data state with the fet
ched data
} catch (err) {
setError(err.message); // Set the error state if an
y error occurs
Alert.alert('Error', 'Failed to fetch data!'); // S
how an error message
} finally {
setLoading(false); // Stop loading
}
};

fetchData();
}, [url]); // Re-run the effect when the URL changes

// Return the fetched data, loading status, and error mes


sage
return { data, loading, error };
};

export default useFetch;

🔹 Breakdown:
States:

data : Stores the fetched data from the API.

loading : Boolean flag to track whether data is being loaded.

error : Stores any error message if fetching fails.

useEffect Hook: Triggers the fetching of data whenever the component


mounts or the URL changes.

Fetching Data:

Code evolution React hook 153


The fetch API is used to make the request.

We handle errors using a try-catch block and display an alert if fetching


fails.

Returning Values: The hook returns the data , loading , and error states,
which can be used by the component to display relevant information (like
loading indicators or error messages).

2. Using the Custom Hook in a Component


Now, let's use our useFetch custom hook in a React Native component to fetch
and display user data.

// UserList.js
import React from 'react';
import { View, Text, FlatList, ActivityIndicator } from 're
act-native';
import useFetch from './useFetch'; // Import the custom hoo
k

const UserList = () => {


// Using the custom hook to fetch data from the API
const { data, loading, error } = useFetch('https://jsonpl
aceholder.typicode.com/users');

if (loading) {
return (
<View style={{ flex: 1, justifyContent: 'center', ali
gnItems: 'center' }}>
<ActivityIndicator size="large" color="#0000ff" />
<Text>Loading...</Text>
</View>
);
}

if (error) {
return (
<View style={{ flex: 1, justifyContent: 'center', ali
gnItems: 'center' }}>

Code evolution React hook 154


<Text style={{ color: 'red' }}>Error: {error}</Text
>
</View>
);
}

return (
<View style={{ flex: 1, padding: 20 }}>
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={{ marginBottom: 15, padding: 10, bac
kgroundColor: '#f0f0f0', borderRadius: 5 }}>
<Text>{item.name}</Text>
<Text>{item.email}</Text>
</View>
)}
/>
</View>
);
};

export default UserList;

🔹 Breakdown:
Calling the Custom Hook: We call useFetch with the API URL
https://jsonplaceholder.typicode.com/users to fetch a list of users.

Handling States:

Loading State: If loading is true, we show a ActivityIndicator and a


"Loading..." message.

Error State: If an error occurs (e.g., a failed network request), we


display the error message.

Data State: Once the data is fetched successfully, we display the list of
users using a FlatList .

Code evolution React hook 155


3. Result:
When the app starts, it will display a loading spinner.

Once the data is fetched successfully, it will display the list of users.

If an error occurs (e.g., network failure), it will display an error message.

Why This Custom Hook is Useful 🤩:


Reusability: We can now reuse this useFetch hook across different
components to fetch data from various APIs, reducing the amount of
repeated code.

Separation of Concerns: This keeps the component code clean and


focuses on the UI, while the hook handles the logic of fetching data,
managing loading and error states.

Improved Readability: The logic for handling network requests is


abstracted away in the custom hook, making the component easier to read
and maintain.

Additional Ideas 💡:
Add Retry Logic: Enhance the hook to support retrying failed requests
automatically.

Custom Fetch Hook with Pagination: Modify useFetch to handle paginated


data fetching.

Now you have a powerful custom hook in React Native that can easily be
reused across your app!

Great observation! In React and React Native, you can return either an object
or an array from a custom hook. The approach you choose depends on how
you want to structure the returned values. Let's break it down:

1. Returning an Object from the Hook 🏷️


In our custom hook example (like useFetch ), we return an object because it
allows us to clearly label and access the values using destructuring.

Why an Object?

Code evolution React hook 156


Named properties: You get meaningful names for each property, making
the code more readable and self-descriptive.

Easier to extend: If you want to return more values in the future (like status
or message ), it's easy to add them to the object without changing the way
consumers access the hook.

Example of Returning an Object 🧳


// useFetch.js
import { useState, useEffect } from 'react';

const useFetch = (url) => {


const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

// Returning an object
return { data, loading, error };
};

Code evolution React hook 157


export default useFetch;

In the component:

// UserList.js
import React from 'react';
import { View, Text, FlatList, ActivityIndicator } from 're
act-native';
import useFetch from './useFetch';

const UserList = () => {


const { data, loading, error } = useFetch('https://jsonpl
aceholder.typicode.com/users');

if (loading) return <ActivityIndicator size="large" color


="#0000ff" />;
if (error) return <Text>{error}</Text>;

return (
<FlatList
data={data}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <Text>{item.name}</Text>}
/>
);
};

export default UserList;

2. Returning an Array from the Hook 🍱


Alternatively, you can return an array from a custom hook, especially when you
are returning values that don't need explicit labels. This is often done when you
have simple state and actions, like in the useState hook.

Why an Array?

Code evolution React hook 158


Simplicity: If the returned values are simple (like state and functions) and
their meaning is obvious, you might prefer using an array.

Familiarity with useState : Since useState returns a state variable and its
updater function as an array (e.g., [state, setState] ), using arrays in custom
hooks can feel intuitive.

Example of Returning an Array 📦


// useCounter.js
import { useState } from 'react';

const useCounter = (initialCount = 0) => {


const [count, setCount] = useState(initialCount);

const increment = () => setCount(count + 1);


const decrement = () => setCount(count - 1);
const reset = () => setCount(initialCount);

// Returning an array
return [count, increment, decrement, reset];
};

export default useCounter;

In the component:

// Counter.js
import React from 'react';
import { View, Text, Button } from 'react-native';
import useCounter from './useCounter';

const Counter = () => {


const [count, increment, decrement, reset] = useCounter
(0); // Using destructuring

return (
<View>
<Text>Count: {count}</Text>

Code evolution React hook 159


<Button title="Increment" onPress={increment} />
<Button title="Decrement" onPress={decrement} />
<Button title="Reset" onPress={reset} />
</View>
);
};

export default Counter;

Key Differences Between Object and Array Return 🌈


Return Type When to Use Advantages

- Easier to extend with more


When you need descriptive
Object properties - Clearer meaning when
labels or multiple values.
destructuring

When you have simple


- More concise - Follows the same
Array values or a fixed set of
pattern as useState
values.

Important Takeaways:
Object Return is more flexible and makes the code more readable when
there are multiple return values.

Array Return is great for simpler hooks with a fixed number of return
values, especially if you follow the pattern of useState or useReducer .

Choose what makes the most sense for your hook's purpose! ✨
Now you can make custom hooks both clean and scalable. 🌱

Using an object instead of an array in custom hooks offers several key


benefits that can make your code more maintainable, readable, and flexible.
Here's a breakdown of the advantages of using an object return:

🛠️ 1. Descriptive Naming (Improved Readability)


Objects allow you to give clear and descriptive names to the values or
methods returned, which makes the code easier to understand.

Code evolution React hook 160


💡 Example:
return { count, increment, decrement, reset }

The names count , increment , decrement , and reset make it immediately clear
what each value or function represents. This is self-documenting code.
Vs. an array:

return [count, increment, decrement, reset];

While this works, it's less intuitive because the names of each item in the
array are not immediately apparent.

🔍 2. Easy to Extend (Scalability)


When you need to add more functionality or values to a hook, you don't
need to worry about changing the order of elements in an array. With an
object, you simply add new properties.

💡 Example (adding a new value):


return { count, increment, decrement, reset, isLoading }

In an array, you'd have to carefully manage the order and ensure


components that use the hook correctly interpret each element's position.
Array (less clear as you add more items):

return [count, increment, decrement, reset, isLoading];

Adding more items may lead to confusion when the array grows.

🔄 3. No Need for Destructuring by Index


With arrays, you must destructure based on the order of the return values.
If you add or remove items in the future, all destructured components in
your code will break.

💡 Example with Object:


const { count, increment, reset } = useCounter();

Code evolution React hook 161


The names ( count , increment , reset ) are fixed, no matter how many
properties you add to the object later.

Vs. Array Destructuring:

const [count, increment, reset] = useCounter();

If you change the order of items in the returned array later, you'll need to
update every destructured variable throughout your app.

⚙️ 4. Easier Maintenance and Refactoring


When you use objects, refactoring is easier because you can change the
function's return values without worrying about their positions in an array.
Objects also give you better control over adding optional values without
breaking the API.

💡 Example:
return { count, increment, decrement, reset, timestamp }

You can add more functionality over time (like timestamp ), and the rest of the
code won't need to change, keeping the hook backward-compatible.

🔄 5. Self-Descriptive Key Names


With objects, the key names themselves describe the value or method
being returned, improving code readability. This is helpful when the hook
returns multiple related pieces of data.

💡 Example:
return { count, increment, decrement, reset }

Each key clearly explains the function of the value or method returned. This
self-documents the hook and makes it clear for others (or your future self)
what the return values are.

🧩 6. Easier to Use in Larger Components


When you're working in larger components that have multiple hooks or
states, objects allow you to keep track of multiple pieces of state without
worrying about the order of items.

Code evolution React hook 162


💡 Example:
const { count, increment, decrement, reset } = useCounte
r();
const { name, setName } = useName();

Here, you can quickly see what each custom hook returns, and it's easier to
manage multiple hooks without confusion.

⚡ 7. Avoiding Index-based Bugs


Arrays require you to rely on index-based access when destructuring. If
you mistakenly swap two items in the return array, you can introduce bugs
that are harder to track down.
Object (safer):

const { count, increment } = useCounter();

Array (risk of index-based bugs):

const [count, increment] = useCounter(); // Mistaking th


e order can cause issues

📚 8. Better Compatibility with Libraries or APIs


Some libraries or patterns expect or work better with objects because they
often require passing data with specific keys. Using an object in your
custom hook allows you to follow those patterns more easily.

🏆 Summary of Benefits:
Readability: Objects give descriptive names to values, making the code
more readable.

Extensibility: Objects are easier to extend with new properties without


breaking the hook's usage.

Safer Refactoring: Changing or adding properties in objects is safer and


doesn't break destructuring or require reordering.

Code evolution React hook 163


Error Reduction: Objects reduce the chances of bugs related to incorrect
indexing.

Scalability: Objects grow naturally, making the code more flexible for future
changes.

🎨 When to Use Objects in Custom Hooks?


Use objects when:

Your hook is returning multiple related values (state, actions, etc.).

You want to improve the clarity and maintainability of the hook.

You plan to extend the hook with more functionality over time.

So, while arrays are great for simpler cases (like useState ), objects are a more
robust, scalable, and user-friendly choice when building custom hooks in
React and React Native! 😎✨

Code evolution React hook 164

You might also like