React hooks are powerful tools for managing state and side effects in your applications. While hooks like useState
, useEffect
, and useReducer
are built into React, it's often beneficial to create custom hooks tailored to your specific needs. Here are three essential custom hooks that you should consider adding to your project:
1. useFetch
Handling requests to a backend server is a common task in almost every project. Instead of using the native JavaScript fetch
function repeatedly, you can create a custom hook to manage this logic:
import { useEffect, useState } from "react";
/**
* Custom hook to fetch data from a given URL.
*
* @template T - The type of the data being fetched.
* @param {string} url - The URL to fetch data from.
* @param {RequestInit | undefined} options - The options to pass to the fetch request.
* @returns {[T | null, boolean, unknown | null]} - An array containing the fetched data, loading state, and error state.
*/
export function useFetch<T>(url: string, options?: RequestInit): [T | null, boolean, unknown | null] {
// State to store the fetched data
const [data, setData] = useState<T | null>(null);
// State to store the loading status
const [isLoading, setIsLoading] = useState<boolean>(false);
// State to store any error that occurs during the fetch
const [error, setError] = useState<unknown | null>(null);
// useEffect hook to trigger the fetch operation whenever the URL or options change
useEffect(() => {
fetchData();
}, [url, options]);
/**
* Function to fetch data from the provided URL.
*
* This function handles the fetch operation, updates the loading state,
* sets the fetched data, and handles any errors that occur during the fetch.
*/
async function fetchData(): Promise<void> {
// Set loading state to true before starting the fetch
setIsLoading(true);
try {
// Perform the fetch operation
const response = await fetch(url, options);
// Parse the response data as JSON
const responseData = await response.json();
// Update the state with the fetched data
setData(responseData);
// Clear any previous error
setError(null);
} catch (error) {
// Update the state with the error if the fetch fails
setError(error);
} finally {
// Set loading state to false after the fetch is complete
setIsLoading(false);
}
}
// Return the fetched data, loading state, and error state
return [data, isLoading, error];
}
2. useLocalStorage
Storing data on the client side, such as in localStorage
, is a common requirement for caching and saving useful information. This custom hook helps you get and set data in localStorage
:
import { useEffect, useState } from "react";
/**
* A custom hook that synchronizes a state variable with localStorage.
*
* @template T The type of the stored value.
* @param {string} key The key under which the value is stored in localStorage.
* @returns {[T | null, (value: T) => void]} A stateful value and a function to update it.
*
* @example
* const [name, setName] = useLocalStorage<string>('name');
* setName('Alice');
* console.log(name); // 'Alice'
*/
export function useLocalStorage<T>(
key: string,
): [T | null, (value: T) => void] {
// Initialize state with null
const [data, setData] = useState<T | null>(() => null);
// useEffect to retrieve and set the data from localStorage on mount and when the key changes
useEffect(() => {
setData(() => {
// Get the data from localStorage
const localStorageData = localStorage.getItem(key);
// If data exists, parse and return it; otherwise, return null
if (localStorageData) {
return JSON.parse(localStorageData);
} else {
return null;
}
});
}, [key]); // Dependency array with key ensures this runs when key changes
/**
* Updates the localStorage value and the state.
*
* @param {T} value The value to store.
*/
function setLocalStorage(value: T): void {
// Store the value in localStorage
localStorage.setItem(key, JSON.stringify(value));
// Update the state to trigger rerender
setData(() => value);
}
// Return the stateful value and the updater function
return [data, setLocalStorage];
}
3. useToggle
One of the simplest yet most useful hooks is useToggle
. This hook manages a boolean state, making it easy to toggle between true
and false
. This is particularly useful for UI elements like dropdowns:
import { useState, Dispatch, SetStateAction } from "react";
/**
* A custom hook that provides a boolean state with a toggle function.
*
* @param {boolean} [initialValue=false] The initial value of the toggle state.
* @returns {[boolean, () => void, Dispatch<SetStateAction<boolean>>]} An array containing:
* - The current boolean state.
* - A function to toggle the state.
* - The state setter function.
*
* @example
* const [isOpen, toggleIsOpen, setIsOpen] = useToggle(false);
* toggleIsOpen(); // Toggles the state
* console.log(isOpen); // true or false
*/
export function useToggle(
initialValue?: boolean
): [boolean, () => void, Dispatch<SetStateAction<boolean>>] {
// Initialize the state with the initial value or default to false
const [isToggle, setIsToggle] = useState<boolean>(() => initialValue ?? false);
/**
* Function to toggle the boolean state.
*/
function toggle(): void {
// Toggle the state value
setIsToggle(value => !value);
}
// Return the current state, the toggle function, and the state setter
return [isToggle, toggle, setIsToggle];
}
Conclusion
Understanding and utilizing React hooks is key to creating powerful and efficient applications. While it's common to write logic directly inside React components, repetitive patterns suggest it's time to create custom hooks. With useFetch
, useLocalStorage
, and useToggle
, you can enhance your toolkit and streamline your development process.
What custom hook do you use the most? Let me know in the comments below! :D
Top comments (2)
nice Work Keep it up !
Thanks for the feedback