Custom Hooks for Reusability
Key Concepts
- What is a Custom Hook?
- Creating a Custom Hook
- State Management with Custom Hooks
- Data Fetching with Custom Hooks
- Form Handling with Custom Hooks
- Event Handling with Custom Hooks
- Local Storage with Custom Hooks
- Debouncing and Throttling with Custom Hooks
- Responsive Design with Custom Hooks
- Authentication with Custom Hooks
- Error Handling with Custom Hooks
- Real-world Examples
What is a Custom Hook?
A Custom Hook is a JavaScript function that starts with the word "use" and can call other Hooks. Custom Hooks allow you to extract component logic into reusable functions, making your code more modular and easier to maintain.
Creating a Custom Hook
To create a Custom Hook, define a function that starts with "use" and encapsulate the logic you want to reuse. This function can then be called from any component that needs the logic.
Example:
function useToggle(initialValue = false) { const [value, setValue] = useState(initialValue); const toggle = () => setValue(!value); return [value, toggle]; }
State Management with Custom Hooks
Custom Hooks can manage state by encapsulating stateful logic and returning the state and any functions to update it. This allows you to reuse state management logic across multiple components.
Example:
function useCounter(initialValue = 0) { const [count, setCount] = useState(initialValue); const increment = () => setCount(count + 1); const decrement = () => setCount(count - 1); return { count, increment, decrement }; }
Data Fetching with Custom Hooks
Custom Hooks can handle data fetching by encapsulating the logic for making API requests and managing the loading and error states. This allows you to reuse data fetching logic across multiple components.
Example:
function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { fetch(url) .then(response => response.json()) .then(data => { setData(data); setLoading(false); }) .catch(error => { setError(error); setLoading(false); }); }, [url]); return { data, loading, error }; }
Form Handling with Custom Hooks
Custom Hooks can handle form state and validation by encapsulating the logic for managing form inputs and validation rules. This allows you to reuse form handling logic across multiple forms.
Example:
function useForm(initialValues) { const [values, setValues] = useState(initialValues); const [errors, setErrors] = useState({}); const handleChange = e => { setValues({ ...values, [e.target.name]: e.target.value }); }; const validate = () => { const newErrors = {}; if (!values.email) newErrors.email = 'Email is required'; if (!values.password) newErrors.password = 'Password is required'; setErrors(newErrors); return Object.keys(newErrors).length === 0; }; return { values, errors, handleChange, validate }; }
Event Handling with Custom Hooks
Custom Hooks can handle event listeners by encapsulating the logic for adding and removing event listeners. This allows you to reuse event handling logic across multiple components.
Example:
function useEventListener(eventName, handler, element = window) { useEffect(() => { element.addEventListener(eventName, handler); return () => { element.removeEventListener(eventName, handler); }; }, [eventName, handler, element]); }
Local Storage with Custom Hooks
Custom Hooks can handle local storage by encapsulating the logic for reading and writing data to local storage. This allows you to reuse local storage logic across multiple components.
Example:
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 { 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]; }
Debouncing and Throttling with Custom Hooks
Custom Hooks can handle debouncing and throttling by encapsulating the logic for delaying function calls. This allows you to reuse debouncing and throttling logic across multiple components.
Example:
function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; }
Responsive Design with Custom Hooks
Custom Hooks can handle responsive design by encapsulating the logic for detecting screen size and orientation. This allows you to reuse responsive design logic across multiple components.
Example:
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; }
Authentication with Custom Hooks
Custom Hooks can handle authentication by encapsulating the logic for checking authentication status and managing user sessions. This allows you to reuse authentication logic across multiple components.
Example:
function useAuth() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const checkAuth = async () => { try { const user = await fetchUser(); setUser(user); } catch (error) { setUser(null); } finally { setLoading(false); } }; checkAuth(); }, []); return { user, loading }; }
Error Handling with Custom Hooks
Custom Hooks can handle error handling by encapsulating the logic for catching and handling errors. This allows you to reuse error handling logic across multiple components.
Example:
function useErrorHandler() { const [error, setError] = useState(null); const handleError = error => { setError(error); console.error(error); }; return { error, handleError }; }
Real-world Examples
Real-world examples of Custom Hooks include:
- A Custom Hook for managing a shopping cart state
- A Custom Hook for handling geolocation
- A Custom Hook for managing a theme switcher
- A Custom Hook for handling notifications
- A Custom Hook for managing a multi-step form
Analogies
Think of Custom Hooks as reusable recipes in a cookbook. Just as a recipe can be used to prepare a dish multiple times, a Custom Hook can be used to encapsulate logic that can be reused across multiple components. Each recipe (Custom Hook) can be applied to different dishes (components), making the process of adding functionality more efficient.
Another analogy is a toolbox. Just as a toolbox contains multiple tools that can be used for different tasks, Custom Hooks contain reusable logic that can be used for different components. Each tool (Custom Hook) can be applied to different tasks (components), making the process of adding functionality more efficient.