React, a popular JavaScript library for building user interfaces revolutionized front-end development by introducing hooks in version 16.8. Hooks allow developers to use state and other React features without writing a class. One of the most powerful features of hooks is the ability to create custom hooks, enabling the encapsulation and reuse of logic across multiple components.
This article will explore the power of custom React hooks with seven practical examples showcasing their versatility and benefits.
1. useFetch: Simplifying Data Fetching
Fetching data from an API is a common requirement in web applications. The useFetch
custom hook abstracts the logic for fetching data, managing loading states, and handling errors.
Implementation
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
setData(null);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
export default useFetch;
Usage
import React from 'react';
import useFetch from './useFetch';
const PostList = () => {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<ul>
{data.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
};
export default PostList;
2. useLocalStorage: Managing Local Storage
Managing local storage can be tedious and error-prone. The useLocalStorage
hook simplifies this by providing an easy interface for getting and setting values in local storage.
Implementation
import { useState } from 'react';
const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(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.log(error);
}
};
return [storedValue, setValue];
};
export default useLocalStorage;
Usage
import React from 'react';
import useLocalStorage from './useLocalStorage';
const Counter = () => {
const [count, setCount] = useLocalStorage('count', 0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
};
export default Counter;
3. useDebounce: Handling Debounced Values
Debouncing is a technique that limits the rate at which a function is executed. The useDebounce
hook helps manage debounced values and is useful for search inputs and other real-time filtering operations.
Implementation
import { useState, useEffect } from 'react';
const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
};
export default useDebounce;
Usage
import React, { useState } from 'react';
import useDebounce from './useDebounce';
const SearchInput = ({ onSearch }) => {
const [input, setInput] = useState('');
const debouncedInput = useDebounce(input, 500);
useEffect(() => {
onSearch(debouncedInput);
}, [debouncedInput, onSearch]);
return (
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Search..."
/>
);
};
export default SearchInput;
4. usePrevious: Accessing Previous State
Sometimes you need to access the previous state value. The usePrevious
hook allows you to do this easily.
Implementation
import { useEffect, useRef } from 'react';
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
export default usePrevious;
Usage
import React, { useState, useEffect } from 'react';
import usePrevious from './usePrevious';
const Counter = () => {
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
useEffect(() => {
if (prevCount !== undefined) {
console.log(`Previous count: ${prevCount}`);
}
}, [count, prevCount]);
return (
<div>
<p>Current Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default Counter;
5. useWindowSize: Tracking Window Size
Responsive design often requires knowing the window size. The useWindowSize
hook provides an easy way to track window dimensions.
Implementation
import { useState, useEffect } from 'react';
const 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;
Usage
import React from 'react';
import useWindowSize from './useWindowSize';
const WindowSizeComponent = () => {
const { width, height } = useWindowSize();
return (
<div>
<p>Width: {width}px</p>
<p>Height: {height}px</p>
</div>
);
};
export default WindowSizeComponent;
6. useOnlineStatus: Detecting Online/Offline Status
Detecting the user’s online or offline status can be useful for building resilient web applications. The useOnlineStatus
hook helps track this status.
Implementation
import { useState, useEffect } from 'react';
const useOnlineStatus = () => {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
};
export default useOnlineStatus;
</code>
Usage
import React from 'react';
import useOnlineStatus from './useOnlineStatus';
const OnlineStatusComponent = () => {
const isOnline = useOnlineStatus();
return (
<div>
<p>You are currently {isOnline ? 'online' : 'offline'}.</p>
</div>
);
};
export default OnlineStatusComponent;
7. useHover: Detecting Hover State
Detecting whether an element is being hovered can enhance user interactions. The useHover
hook manages the hover state for any element.
Implementation
import { useState, useRef, useEffect } from 'react';
const useHover = () => {
const [hovered, setHovered] = useState(false);
const ref = useRef(null);
const handleMouseOver = () => setHovered(true);
const handleMouseOut = () => setHovered(false);
useEffect(() => {
const node = ref.current;
if (node) {
node.addEventListener('mouseover', handleMouseOver);
node.addEventListener('mouseout', handleMouseOut);
return () => {
node.removeEventListener('mouseover', handleMouseOver);
node.removeEventListener('mouseout', handleMouseOut);
};
}
}, [ref.current]);
return [ref, hovered];
};
export default useHover;
Usage
import React from 'react';
import useHover from './useHover';
const HoverComponent = () => {
const [hoverRef, isHovered] = useHover();
return (
<div>
<div ref={hoverRef} style={{ width: '100px', height: '100px', backgroundColor: isHovered ? 'blue' : 'gray' }}>
Hover over me!
</div>
</div>
);
};
export default HoverComponent;
Wrapping Up
Custom React hooks offer a powerful way to encapsulate and reuse logic across your components, promoting cleaner and more maintainable code. Abstracting repetitive tasks into custom hooks can enhance your application’s functionality and improve developer productivity.
The seven examples provided—useFetch
, useLocalStorage
, useDebounce
, usePrevious
, useWindowSize
, useOnlineStatus
, and useHover
—demonstrate the versatility and utility of custom hooks in various scenarios. As you develop more complex applications, consider leveraging custom hooks to simplify your code and enhance your development workflow.
Leave a Reply