Custom hooks in React are JavaScript functions that allow you to reuse stateful logic across multiple components. They enable you to abstract common functionality into reusable pieces, making your code more modular, readable, and maintainable.
1. What Are Custom Hooks?
Custom hooks are functions that can call other hooks (like useState
, useEffect
, etc.) and return values or functions that can be used by components. They allow you to extract and reuse stateful logic, making your components cleaner and reducing code duplication.
Custom hooks are prefixed with “use” to follow React’s convention, which allows React to differentiate hooks from normal JavaScript functions.
Example of a Custom Hook:
import { useState, useEffect } 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) {
return initialValue;
}
});
const setValue = (value) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error("Could not store value", error);
}
};
return [storedValue, setValue];
}
In this example:
useLocalStorage
is a custom hook that abstracts away the logic for working withlocalStorage
.- It returns the stored value from
localStorage
and a function to update the value inlocalStorage
.
2. Why Use Custom Hooks?
Custom hooks allow you to:
- Reusability: Extract and reuse stateful logic across multiple components.
- Readability: Organize complex logic into separate functions, making your component code cleaner and easier to read.
- Encapsulation: Hide implementation details of logic while exposing only necessary values or methods to the components using the hook.
- Avoid Duplication: Prevent code duplication by encapsulating common behavior that can be used in multiple places.
3. Example: Using a Custom Hook
Now let’s see how the useLocalStorage
hook can be used in a component.
import React, { useState } from 'react';
import { useLocalStorage } from './useLocalStorage'; // Assuming custom hook is in this file
function App() {
const [name, setName] = useLocalStorage('name', 'John Doe'); // Using the custom hook
const [age, setAge] = useState(25);
return (
<div>
<h1>Custom Hook Example</h1>
<p>Name: {name}</p>
<p>Age: {age}</p>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)} // Updates name in localStorage
/>
<button onClick={() => setAge(age + 1)}>Increase Age</button>
</div>
);
}
export default App;
In this example:
useLocalStorage
manages thename
state with localStorage. Any changes toname
will automatically be saved tolocalStorage
and retrieved when the component is rendered.
4. How to Create a Custom Hook
Creating a custom hook follows a simple pattern:
- Define the hook: Create a JavaScript function that starts with
use
(e.g.,useFetch
,useLocalStorage
). - Use built-in hooks: Inside the custom hook, use React’s built-in hooks like
useState
,useEffect
,useRef
, etc. - Return values or functions: Return whatever data or functionality your hook needs to provide to the component.
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);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
This custom hook useFetch
is designed to fetch data from a URL, manage the loading state, and handle any errors that may occur.
You can use it in a component as follows:
import React from 'react';
import { useFetch } from './useFetch'; // Assuming custom hook is in this file
function App() {
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Fetched Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default App;
5. Best Practices for Custom Hooks
- Start with
use
: Always start the custom hook name withuse
(e.g.,useLocalStorage
,useFetch
) to follow React’s naming convention for hooks. - Keep hooks small: A custom hook should do one thing well. If your hook does too many things, consider breaking it into smaller hooks.
- Use multiple hooks: Custom hooks can use other hooks (like
useState
,useEffect
, etc.) inside them, making them a flexible way to combine logic. - Return only necessary data: Return only what is needed by the component that uses the hook. This could be state, functions, or data.
- Avoid side effects outside
useEffect
: If your custom hook requires side effects (e.g., API calls, localStorage), make sure to useuseEffect
to handle those.