Understanding Custom Hooks
Custom Hooks are a way to extract component logic into reusable functions. They allow you to share stateful logic between components without changing your component hierarchy.
By creating custom hooks, you can encapsulate complex logic, make your components cleaner, and promote code reuse across your application.
Creating Custom Hooks
Custom hooks are JavaScript functions that start with "use" and may call other hooks. They follow the same rules as regular hooks and can use any of the built-in hooks.
Key Points:
- Must start with "use"
- Can use other hooks
- Should be pure functions
- Can return any value
Example: useWindowSize Hook
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return windowSize;
}
// Using the custom hook
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
Window size: {width} x {height}
</div>
);
}
Common Custom Hooks
1. useLocalStorage Hook
A hook for persisting state in localStorage with automatic synchronization.
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) {
console.error(error);
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch (error) {
console.error(error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
// Usage
function UserPreferences() {
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<div>
<select value={theme} onChange={e => setTheme(e.target.value)}>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
2. useFetch Hook
A hook for handling data fetching with loading and error states.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return null;
return (
<div>
<h2>{data.name}</h2>
<p>{data.email}</p>
</div>
);
}
3. useDebounce Hook
A hook for debouncing values, useful for search inputs and other frequent updates.
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchInput() {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
// Perform search
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
Custom Hooks Best Practices
1. Single Responsibility
Each custom hook should do one thing and do it well. If a hook is doing too many things, consider breaking it down into smaller hooks.
2. Clear Naming
Name your hooks clearly and descriptively. The name should indicate what the hook does and what it returns.
3. Proper Cleanup
Always clean up subscriptions, event listeners, and other side effects in the useEffect cleanup function.
4. Documentation
Document your custom hooks with clear comments explaining their purpose, parameters, and return values.
Next Steps
Now that you understand custom hooks, you might want to explore: