Custom Hooks in React allow you to reuse stateful logic across multiple components. They are essentially JavaScript functions that use one or more React hooks (useState
, useEffect
, etc.) to encapsulate and share functionality. Custom Hooks help you keep your components clean and modular by abstracting repetitive logic.
Why Use Custom Hooks?
- Reusability: Encapsulate logic that can be shared across multiple components.
- Clean Code: Separate logic from component UI, making components easier to read and maintain.
- Abstraction: Keep components focused on their primary responsibility by moving complex logic into hooks.
Rules for Custom Hooks
- Custom Hooks are named with the prefix
use
(e.g.,useFetch
,useLocalStorage
). - They must follow the Rules of Hooks, such as:
- Only call hooks at the top level.
- Only call hooks inside React function components or other custom hooks.
Syntax of a Custom Hook
function useCustomHook(parameters) { // Use React hooks like useState, useEffect, etc. const [state, setState] = useState(initialValue); // Define logic here useEffect(() => { // Perform some operation }, [dependencies]); // Return data or functions for the component to use return [state, setState]; }
Example: Custom Hook for Fetching Data
import { useState, useEffect } from "react"; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); if (!response.ok) { throw new Error("Failed to fetch"); } const result = await response.json(); setData(result); } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; } export default useFetch;
Using the Custom Hook
import React from "react"; import useFetch from "./useFetch"; function App() { const { data, loading, error } = useFetch("https://api.example.com/data"); if (loading) return <p>Loading...</p>; if (error) return <p>Error: {error}</p>; return ( <div> <h1>Fetched Data:</h1> <ul> {data.map((item) => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> ); } export default App;
Example: Custom Hook for Managing Local Storage
import { useState } from "react"; function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.error(error); return initialValue; } }); const setValue = (value) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key, JSON.stringify(valueToStore)); } catch (error) { console.error(error); } }; return [storedValue, setValue]; } export default useLocalStorage;
Using the Custom Hook
import React from "react"; import useLocalStorage from "./useLocalStorage"; function App() { const [name, setName] = useLocalStorage("name", ""); return ( <div> <h1>Custom Hook Example: Local Storage</h1> <input type="text" placeholder="Enter your name" value={name} onChange={(e) => setName(e.target.value)} /> <p>Stored Name: {name}</p> </div> ); } export default App;
Example: Custom Hook for Window Resize
import { useState, useEffect } from "react"; function useWindowSize() { const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight, }); useEffect(() => { const handleResize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); return windowSize; } export default useWindowSize;