- Published on
All Hooks in ReactJs
- Authors
- Name
- Ganesh Negi
React Hooks
React Hooks revolutionized the way we manage state and side effects in functional components.
Introduced in React 16.8, they have become an essential part of modern React development, replacing many class-based patterns.
In this blog, we'll dive into some of the most frequently used React hooks, breaking them down with simple examples to help you understand their practical applications.

The useState Hook
The useState hook lets you add state management to functional components. It accepts an initial state value and returns an array containing two elements: the current state and a function to update it.
Let’s look at a simple example where we create a counter component using useState:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div>
<p>Current Count: {count}</p>
<button onClick={increment}>Increase</button>
<button onClick={decrement}>Decrease</button>
</div>
);
}
export default Counter;
In this example, the state variable count starts at 0. Clicking the "Increase" button increments the count, while the "Decrease" button decrements it.
The component re-renders whenever the state updates, ensuring the UI stays in sync.

useEffect hook
The useEffect hook enables functional components to handle side effects such as fetching data, interacting with the DOM, or setting up event listeners.
It runs after the component renders and can be configured to run at specific times.
Here’s an example demonstrating how to use useEffect to fetch data from an API:
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, []);
return (
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
export default DataFetcher;
How It Works
We use useState to store the fetched data.
Inside useEffect, we define an asynchronous function fetchData to call the API and update state.
The empty dependency array ([]) ensures the effect runs only once when the component mounts.
This approach ensures clean and efficient data fetching while handling potential errors.
The useContext Hook
The useContext hook allows functional components to consume values from a context without the need for prop drilling.
Context provides a way to share global data—such as themes, authentication status, or user preferences—throughout the component tree.
Example: Using useContext for Theme Management
import React, { useContext } from 'react';
// Create a Theme Context with a default value of "light"
const ThemeContext = React.createContext('light');
function ThemeButton() {
// Access the current theme value from context
const theme = useContext(ThemeContext);
return (
<button
style={{
background: theme === 'dark' ? 'black' : 'white',
color: theme === 'dark' ? 'white' : 'black',
}}
>
{theme === 'dark' ? 'Dark Mode' : 'Light Mode'}
</button>
);
}
export default ThemeButton;
How It Works
- We define a ThemeContext using React.createContext(), setting "light" as the default theme.
- Inside ThemeButton, the useContext(ThemeContext) hook retrieves the current theme value.
- The button’s styles dynamically change based on the theme value.
- To provide a dynamic theme, wrap the component in a ThemeContext.Provider with a specified value:
<ThemeContext.Provider value="dark">
<ThemeButton />
</ThemeContext.Provider>
This method makes it easy to manage themes across multiple components without manually passing props.
The useReducer Hook
The useReducer hook is a powerful alternative to useState, particularly when managing complex state logic.
Instead of directly updating state, useReducer relies on a reducer function to determine how state should change based on dispatched actions.
Example: Managing a Shopping Cart with useReducer
import React, { useReducer } from 'react';
// Reducer function to handle cart actions
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return [...state, action.payload];
case 'REMOVE_ITEM':
return state.filter((item) => item.id !== action.payload.id);
default:
return state;
}
}
function ShoppingCart() {
// Initialize state with useReducer
const [cart, dispatch] = useReducer(cartReducer, []);
// Function to add an item to the cart
const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', payload: item });
};
// Function to remove an item from the cart
const removeItem = (item) => {
dispatch({ type: 'REMOVE_ITEM', payload: item });
};
return (
<div>
<h2>Shopping Cart</h2>
<ul>
{cart.map((item) => (
<li key={item.id}>
{item.name} - ${item.price}
<button onClick={() => removeItem(item)}>Remove</button>
</li>
))}
</ul>
<button onClick={() => addItem({ id: 1, name: 'Item 1', price: 9.99 })}>
Add Item
</button>
</div>
);
}
export default ShoppingCart;
How It Works
- Defining the Reducer: The cartReducer function handles different action types (ADD_ITEM and REMOVE_ITEM) and updates the state accordingly.
- Using useReducer: The hook initializes state (cart) and provides a dispatch function to trigger state updates.
- Dispatching Actions: The addItem and removeItem functions dispatch actions to modify the cart state.
- Rendering the UI: The component displays cart items and includes buttons to add or remove products dynamically.
- This approach ensures better state management, especially when dealing with complex updates.
The useCallback Hook
The useCallback hook helps optimize performance by memoizing functions.
It ensures that a function reference remains the same between renders unless its dependencies change.
This is particularly useful when passing functions to child components, preventing unnecessary re-renders.
Example: Optimizing a Search Bar with useCallback
import React, { useState, useCallback } from 'react';
function SearchBar({ onSearch }) {
const [query, setQuery] = useState('');
// Memoizing the function so it's only recreated when 'onSearch' changes
const handleQueryChange = useCallback(
(event) => {
setQuery(event.target.value);
onSearch(event.target.value);
},
[onSearch] // Dependency array ensures function is recreated only when 'onSearch' changes
);
return <input type="text" value={query} onChange={handleQueryChange} />;
}
export default SearchBar;
How It Works
- State Management: The query state stores the input field’s value.
- Memoizing handleQueryChange: The useCallback hook ensures that the handleQueryChange function is only re-created when the onSearch function changes.
- Preventing Unnecessary Re-Renders: If onSearch remains the same, React reuses the existing function reference, optimizing performance.
This approach is particularly useful when passing functions as props to child components, as it prevents unnecessary re-renders and enhances efficiency.
The useMemo Hook
The useMemo hook optimizes performance by memoizing expensive computations.
It ensures that a computed value is only recalculated when its dependencies change, preventing unnecessary recalculations on every render.
Example: Optimizing an Expensive Calculation with useMemo
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ a, b }) {
// Memoizing the result to avoid unnecessary recalculations
const result = useMemo(() => {
console.log('Performing heavy calculation...');
return a * b;
}, [a, b]); // Recalculates only when 'a' or 'b' changes
return <p>Result: {result}</p>;
}
export default ExpensiveCalculation;
How It Works
- Defining the Computation: The useMemo hook wraps an expensive calculation (a * b) inside a memoized function.
- Optimizing Performance: The computation is only re-executed when a or b changes, avoiding unnecessary recalculations on re-renders.
- Debugging Insight: The console.log('Performing heavy calculation...') statement helps verify that the function runs only when needed.
When to Use useMemo
- ✅ Optimizing expensive computations (e.g., filtering large datasets, performing complex calculations).
- ✅ Preventing unnecessary re-renders in performance-sensitive applications.
- ✅ Improving efficiency in components with frequently changing props or state.
By using useMemo, we ensure that computationally expensive operations are efficiently handled, enhancing the overall performance of the React application.
The useRef Hook
The useRef hook creates a persistent reference to a DOM element or value without causing re-renders.
It’s commonly used to manage focus, store previous values, or interact with elements directly.
Example: Using useRef to Control Input Focus
import React, { useRef } from 'react';
function InputWithFocus() {
// Create a reference to the input element
const inputRef = useRef(null);
// Function to focus the input field when the button is clicked
const handleFocus = () => {
inputRef.current?.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default InputWithFocus;
How It Works
- Creating a Ref: The useRef hook initializes a reference (inputRef) to store a mutable reference to the input element.
- Accessing the DOM: The ref attribute links the reference to the input field.
- Triggering Focus: When the button is clicked, inputRef.current.focus() programmatically focuses the input field.
When to Use useRef
- ✅ Managing focus (e.g., automatically focusing an input field).
- ✅ Storing previous values without triggering re-renders.
- ✅ Interacting with DOM elements in an efficient way.
The useRef hook is a powerful tool for managing elements that don’t require state changes, improving performance and user interactions.
The useLayoutEffect Hook
The useLayoutEffect hook works similarly to useEffect, but it runs synchronously after the DOM updates, before the browser repaints the screen.
This makes it ideal for measuring elements, handling animations, or making immediate DOM adjustments.
Example: Measuring a Resizable Box with useLayoutEffect
import React, { useState, useLayoutEffect, useRef } from 'react';
function ResizableBox() {
const [size, setSize] = useState({ width: 100, height: 100 });
const boxRef = useRef(null);
useLayoutEffect(() => {
const updateSize = () => {
if (boxRef.current) {
setSize({
width: boxRef.current.clientWidth,
height: boxRef.current.clientHeight,
});
}
};
updateSize(); // Set initial size
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, []);
return (
<div ref={boxRef} style={{ width: '50%', height: '50%', backgroundColor: 'red' }}>
<p>Width: {size.width}px</p>
<p>Height: {size.height}px</p>
</div>
);
}
export default ResizableBox;
How It Works
- Capturing Element Size: useLayoutEffect ensures the updateSize function runs immediately after the DOM updates, allowing us to capture the correct dimensions.
- Using a Ref: boxRef references the div element, enabling us to read its size dynamically.
- Handling Window Resizing: The effect adds a resize event listener to update the size when the window resizes.
- Removing Event Listeners: The cleanup function ensures we avoid memory leaks by removing the event listener when the component unmounts.
When to Use useLayoutEffect
- ✅ Measuring element dimensions (e.g., capturing size before animations).
- ✅ Handling synchronous DOM updates before the browser paints.
- ✅ Making UI adjustments that depend on precise layout measurements.
Unlike useEffect, which runs asynchronously, useLayoutEffect ensures DOM manipulations happen immediately, making it ideal for performance-critical updates.
The useDebugValue Hook
The useDebugValue hook is a developer-focused React hook that enhances debugging for custom hooks.
It enables custom labels in React DevTools, helping developers track hook states efficiently without affecting performance.
Example: Debugging a Custom Data Fetching Hook with useDebugValue
import { useState, useEffect, useDebugValue } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => setData(data));
}, [url]);
// Display debugging information in React DevTools
useDebugValue(data ? `Loaded ${data.length} items` : 'Fetching data...');
return data;
}
How It Works
- Creating a Custom Hook: useFetch(url) fetches data and updates the data state.
- Adding Debugging Info: useDebugValue provides a human-readable label in React DevTools, showing whether data is loading or loaded.
- Enhancing Debugging: When inspecting components that use useFetch, developers can see real-time data status directly in React DevTools.
When to Use useDebugValue
- ✅ Debugging custom hooks in React DevTools.
- ✅ Providing insights into hook behavior without impacting performance.
- ✅ Simplifying development by tracking state changes more effectively.
⚠️ Important: useDebugValue is purely for debugging and does not affect the logic or behavior of the hook itself.
By integrating useDebugValue, developers gain deeper visibility into custom hooks, making debugging more intuitive and efficient.
The useImperativeHandle Hook
The useImperativeHandle hook lets you customize what gets exposed to parent components when using ref.
This is particularly useful when you want to limit access to certain properties and methods of a child component while still offering controlled interactions.
Example: Exposing Custom Methods with useImperativeHandle
import React, { useRef, useImperativeHandle } from 'react';
const CustomInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => inputRef.current.focus(),
getValue: () => inputRef.current.value,
}));
return <input type="text" ref={inputRef} placeholder={props.placeholder} />;
});
function App() {
const inputRef = useRef();
const handleFocus = () => {
inputRef.current.focus();
};
const handleGetValue = () => {
alert(`Input Value: ${inputRef.current.getValue()}`);
};
return (
<div>
<CustomInput ref={inputRef} placeholder="Type something..." />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleGetValue}>Get Input Value</button>
</div>
);
}
export default App;
How It Works
- Creating a Forwarded Ref Component: CustomInput is wrapped with React.forwardRef to allow parent components to pass a ref.
- Using useImperativeHandle: The hook exposes a limited set of methods (focus and getValue), instead of exposing the entire input element.
- Interacting from the Parent: The App component can now call inputRef.current.focus() and inputRef.current.getValue(), but cannot access other internal properties of the input.
When to Use useImperativeHandle
- ✅ Encapsulating child component logic while allowing specific interactions.
- ✅ Creating reusable custom inputs with controlled external access.
- ✅ Preventing unnecessary exposure of child component implementation details.
⚠️ Important: useImperativeHandle should only be used when absolutely necessary. In most cases, using ref directly without customization is sufficient.
By leveraging useImperativeHandle, you ensure controlled access to child components, leading to cleaner and more maintainable React applications.
Final Thoughts on React Hooks
With these powerful React hooks in your toolkit, you can elevate your web development skills and build applications that are more efficient, scalable, and user-friendly.
From managing complex state with useReducer to boosting performance with useMemo and useCallback, React hooks have transformed the way developers write functional components.
Whether you’re a seasoned React expert or just starting out, mastering these hooks will streamline your code, enhance performance, and improve user experience.
By leveraging advanced hooks like useDebugValue and useImperativeHandle, you can create more maintainable, optimized, and extensible applications.
In short, React hooks are an essential tool for modern web development. By incorporating them into your projects, you’ll build faster, cleaner, and more responsive applications that stand out in today’s competitive digital landscape.