As an experienced ReactJS developer, I've encountered numerous hurdles and successes in my application development journey. Over time, I've found that custom React hooks are indispensable for making my code more reusable and efficient. In this article, I'm excited to introduce you to five custom React hooks that I think are essential for every developer. I'll guide you through each hook, offering examples and weaving in some of my own experiences along the way.
Handling browser storage is frequently needed in React applications. The useLocalStorage
hook helps you keep a value in sync with localStorage
. Below is an example of how I utilized it in a recent project:
import { useState, useEffect } from 'react';
const useLocalStorage = (key, defaultValue) => {
const [value, setValue] = useState(() => {
const storedValue = localStorage.getItem(key);
return storedValue ? JSON.parse(storedValue) : defaultValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
In this hook, we set up the state value
to either the stored value from localStorage
or the given default value. We leverage useEffect
to refresh the storage anytime value
gets updated. Here’s an example of how it can be implemented in a component:.
const App = () => {
const [name, setName] = useLocalStorage('name', 'John Doe');
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} />
<p>Hello, {name}!</p>
</div>
);
};
These days, ensuring that your application is responsive and works well on various screen sizes is essential. The useMediaQuery
hook in React makes it simple to manage media queries. Below is an example of how I implemented it:
import { useState, useEffect } from 'react';
const useMediaQuery = (query) => {
const [matches, setMatches] = useState(
() => window.matchMedia(query).matches
);
useEffect(() => {
const mediaQuery = window.matchMedia(query);
const handleChange = (e) => setMatches(e.matches);
mediaQuery.addEventListener('change', handleChange);
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}, [query]);
return matches;
};
In this instance, we'll incorporate an event listener along with matchMedia
to monitor any changes to the media query. Below is an example of how this can be implemented within a component:
const App = () => {
const isMobile = useMediaQuery('(max-width: 768px)');
return (
<div>
<h1>{isMobile ? 'Mobile View' : 'Desktop View'}</h1>
</div>
);
};
Debouncing is a method that postpones the execution of a function until there's been a specific period without any activity. The useDebounce
hook allows you to apply this debouncing technique to any value. Here's an example:
import { useState, useEffect } from 'react';
const useDebounce = (value, delay) => {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
};
This hook lets you debounce user inputs, API calls, and a variety of other actions. Take a look at this practical example:.
const App = () => {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
// Perform search API call with debouncedSearchTerm
// ...
}, [debouncedSearchTerm]);
return (
<div>
<input value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} />
</div>
);
};
Grabbing data asynchronously has become a staple in contemporary web development. The useFetch
hook makes this process easier by streamlining data fetching and managing any errors that might occur. Let's dive into an example.
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, error, loading };
};
This hook simplifies the fetch request process by handling the response, error, and loading states for you. Here's a how-to guide for using it:
const App = () => {
const { data, error, loading } = useFetch('https://api.example.com/data');
if (loading) {
return <p>Loading data...</p>;
}
if (error) {
return <p>Error: {error.message}</p>;
}
return (
<div>
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
};
Handling toggles and boolean states can often be a bit of a hassle. The useToggle
hook makes it effortless to manage boolean states. Below is a straightforward implementation:
import { useState } from 'react';
const useToggle = (initialValue = false) => {
const [value, setValue] = useState(initialValue);
const toggle = () => {
setValue((prevValue) => !prevValue);
};
return [value, toggle];
};
This hook makes it easy to switch between states. Below is an example of its usage:.
const App = () => {
const [isModalOpen, toggleModal] = useToggle(false);
return (
<div>
<button onClick={toggleModal}>Toggle Modal</button>
{isModalOpen && <Modal />}
</div>
);
};
Custom React hooks are incredible resources that can greatly improve your development workflow. This article covered five custom hooks: useLocalStorage
, useMediaQuery
, useDebounce
, useFetch
, and useToggle
. By utilizing these hooks, I’ve managed to simplify my codebase, enhance reusability, and create top-notch applications. I trust you will find these hooks just as beneficial. Happy coding!