Logo Ekbal's Blog

Search Blog

Mastering React Hooks A Comprehensive Guide

May 6, 2025
5 min read
index

Introduction to React Hooks

React Hooks revolutionized functional components by allowing them to manage state and lifecycle features, previously only accessible in class components. This leads to cleaner, more readable, and reusable code. This guide delves into creating custom Hooks, empowering you to abstract complex logic and share it across your React application.

What are React Hooks?

Hooks are functions that let you “hook into” React state and lifecycle features from function components. The official React library provides several built-in Hooks like useState, useEffect, useContext, useReducer, and others. Custom Hooks are JavaScript functions that leverage these built-in Hooks to encapsulate reusable logic.

Why Create Custom Hooks?

  • Reusability: Avoid code duplication by extracting common logic into a reusable Hook.
  • Readability: Simplify components by offloading complex operations to dedicated Hooks.
  • Maintainability: Changes to the Hook’s logic are isolated, minimizing the impact on other parts of the application.
  • Testability: Hooks can be tested independently, ensuring their reliability.

Building Your First Custom Hook: useLocalStorage

Let’s create a custom Hook called useLocalStorage that interacts with the browser’s localStorage. This Hook will allow us to easily store and retrieve data in localStorage while keeping our component code clean.

import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial value to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
if (JSON.parse(item) !== storedValue) {
setValue(JSON.parse(item));
}
} catch (error) {
console.log(error);
}
}, [key]);
return [storedValue, setValue];
}
export default useLocalStorage;

Explanation:

  1. useState: We use useState to manage the value stored in localStorage. The initial value is retrieved from localStorage (if it exists) or defaults to the provided initialValue. We wrap the useState initial value calculation in a function, ensuring the potentially expensive localStorage access only happens once during initialization.
  2. setValue Function: This function updates the state using setStoredValue and persists the new value to localStorage using window.localStorage.setItem. It also handles potential errors during the localStorage interaction. The function checks if the new value is a function to support the same functional updates as useState.
  3. useEffect: This useEffect hook ensures that if the value in localStorage changes outside the direct usage of the Hook, the Hook will synchronize to reflect the changes.
  4. Return Value: The Hook returns an array containing the storedValue and the setValue function, mimicking the return value of useState.

Using the useLocalStorage Hook

Here’s how you can use the useLocalStorage Hook in a React component:

import React from 'react';
import useLocalStorage from './useLocalStorage';
function App() {
const [name, setName] = useLocalStorage('name', '');
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<p>Hello, {name || 'Guest'}!</p>
</div>
);
}
export default App;

In this example, the name state is automatically persisted to localStorage. When the component mounts, the name value is retrieved from localStorage, ensuring the user’s name is remembered even after a page refresh.

Best Practices for Creating Custom Hooks

  • Start with use: Name your custom Hooks with the use prefix. This convention is enforced by React’s linter and helps distinguish them from regular functions.
  • Keep it Focused: Each Hook should encapsulate a single, well-defined piece of logic. Avoid creating overly complex “god” Hooks.
  • Document Your Hooks: Provide clear documentation for each Hook, explaining its purpose, arguments, and return values.
  • Consider Dependencies: Be mindful of dependencies used within useEffect Hooks. Ensure all necessary dependencies are included to avoid unexpected behavior.
  • Test Your Hooks: Write unit tests for your Hooks to ensure they function correctly and reliably.

Advanced Hook Examples

While useLocalStorage provides a solid foundation, let’s briefly consider a few other use cases:

  • useFetch: A Hook that handles data fetching from an API.
  • useWindowSize: A Hook that tracks the window’s width and height.
  • useDebounce: A Hook that delays the execution of a function until after a certain period of inactivity.

The possibilities are endless, limited only by your application’s needs and your creativity.

Conclusion

Creating custom React Hooks is a powerful technique for improving code reusability, readability, and maintainability. By mastering the art of Hook creation, you can build more robust and scalable React applications. Experiment, explore, and discover how Hooks can transform your development workflow!