🚀 Mastering React Hooks and Context API in Functional Components
React Hooks revolutionized how we write components by enabling state and side effects in functional components. In this guide, we’ll explore key React Hooks: useState
, useEffect
, useContext
, cleanup functions, and building global state via Context. We’ll also demonstrate a login/logout flow with context updates.
🔹 What are React Hooks?
Hooks are functions that let you “hook into” React state and lifecycle features from function components. Introduced in React 16.8, Hooks eliminate the need for class components while simplifying code reuse and logic separation.
🧠 useState Hook
The useState
hook lets you add local state to function components. It returns an array with the current state and a function to update it.
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
You can also use multiple useState
declarations:
function ExampleWithManyStates() {
const [name, setName] = useState("Alice");
const [phone, setPhone] = useState("123-4567");
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
}
🧪 useEffect Hook
The useEffect
hook allows you to perform side effects in function components, similar to componentDidMount
, componentDidUpdate
, and componentWillUnmount
in class components.
Here’s a simple useEffect example:
import React, { useState, useEffect } from 'react';
function App() {
const [value, setValue] = useState('blogs');
useEffect(() => {
console.log('You Clicked Me!');
});
return (
<>
<button onClick={() => setValue('blogs')}>Blog Post</button>
<button onClick={() => setValue('participants')}>Participants</button>
<button onClick={() => setValue('feedback')}>Feedback</button>
<h1>{value}</h1>
</>
);
}
🧹 Limiting Execution with Dependencies
If you want useEffect
to run only once (like componentDidMount), pass an empty dependency array:
useEffect(() => {
console.log('Runs only on mount');
}, []);
If you want it to run on specific state changes:
useEffect(() => {
console.log('Feeds updated');
}, [feeds]);
♻️ Cleanup with useEffect
Some effects (like timers or subscriptions) need cleanup to avoid memory leaks.
import { useState, useEffect } from "react";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCount((prev) => prev + 1);
}, 1000);
return () => clearTimeout(timer);
}, []);
return <h1>I've rendered {count} times!</h1>;
}
🌐 Fetching Data with useEffect
// services/list.js
export function getList() {
return fetch('https://gorest.co.in/public/v1/posts')
.then(data => data.json());
}
import React, { useEffect, useState } from 'react';
import { getList } from './services/list';
function App() {
const [list, setList] = useState([]);
useEffect(() => {
let mounted = true;
getList().then(items => {
if (mounted) {
setList(items.data);
}
});
return () => (mounted = false);
}, []);
return (
<div>
<h1>My List</h1>
<ul>
{list.map(item => (
<li key={item.id}><h2>{item.title}</h2></li>
))}
</ul>
</div>
);
}
🧵 useContext Hook
Context lets you share state across components without passing props manually. The useContext
hook simplifies consuming context.
🔧 Creating Context
import { createContext } from 'react';
const MyContext = createContext('Default Value');
📤 Providing Context
function App() {
const value = "My Context Value";
return (
<MyContext.Provider value={value}>
<MyComponent />
</MyContext.Provider>
);
}
📥 Consuming Context
function MyComponent() {
const value = useContext(MyContext);
return <span>{value}</span>;
}
🚫 Prop Drilling vs useContext
// Without context (prop drilling)
function Application() {
const userName = "John Smith";
return <Layout userName={userName} />;
}
function Layout({ userName }) {
return (
<>
<Header userName={userName} />
<main>Main content</main>
</>
);
}
// With context
const UserContext = createContext("Unknown");
function Application() {
return (
<UserContext.Provider value="John Smith">
<Layout />
</UserContext.Provider>
);
}
function UserInfo() {
const userName = useContext(UserContext);
return <span>{userName}</span>;
}
🏗️ Full useContext Example
const employeeContext = React.createContext();
function App() {
const [employee, setEmployee] = useState({
Id: 101,
Name: "Karim",
Location: "Kolkata",
Salary: 12345
});
return (
<employeeContext.Provider value={employee}>
<Employee />
<Salary />
</employeeContext.Provider>
);
}
function Employee() {
const context = useContext(employeeContext);
return (
<div>
<p>ID: {context.Id}</p>
<p>Name: {context.Name}</p>
</div>
);
}
🆙 Updating Context via useState
const ThemeContext = React.createContext(null);
function App() {
const [color, setColor] = useState("Green");
return (
<ThemeContext.Provider value={{ color, setColor }}>
<ChangeColor />
</ThemeContext.Provider>
);
}
function ChangeColor() {
const { color, setColor } = useContext(ThemeContext);
const toggleColor = () => {
setColor(color === "Green" ? "Red" : "Green");
};
return (
<button onClick={toggleColor}>Change Color</button>
);
}
🔐 Authentication Example with useContext
Let’s create a login/logout authentication system using context.
// AuthContext.js
import React from 'react';
const AuthContext = React.createContext({
auth: null,
login: () => {},
logout: () => {},
});
export default AuthContext;
// App.js
import React, { useState } from 'react';
import Login from './Login';
import Logout from './Logout';
import AuthContext from './AuthContext';
function App() {
const [auth, setAuth] = useState(false);
const login = () => setAuth(true);
const logout = () => setAuth(false);
return (
<AuthContext.Provider value={{ auth, login, logout }}>
<p>{auth ? "You are logged in" : "Please log in"}</p>
<Login />
<Logout />
</AuthContext.Provider>
);
}
// Login.js
import React, { useContext } from 'react';
import AuthContext from './AuthContext';
function Login() {
const auth = useContext(AuthContext);
return <button onClick={auth.login}>Login</button>;
}
// Logout.js
import React, { useContext } from 'react';
import AuthContext from './AuthContext';
function Logout() {
const auth = useContext(AuthContext);
return <button onClick={auth.logout}>Logout</button>;
}
✅ Conclusion
React Hooks such as useState
, useEffect
, and useContext
offer powerful ways to manage component logic and state without relying on classes. With the Context API and custom hooks, you can design scalable applications that minimize boilerplate and eliminate prop drilling. By combining these hooks, you can build modern, maintainable, and performant React apps.