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;