Part 73: Leveraging Custom Hooks in React: Simplifying Data Fetching with useUser
[Pages] React Query

React's ecosystem offers a plethora of tools to streamline and enhance component development. Among these, hooks stand out as a pivotal feature, enabling developers to handle side effects and state management efficiently in functional components. In this blog post, we'll explore how to create a custom hook, useUser, to encapsulate data-fetching logic, making your codebase more organized and reusable.
The Power of Custom Hooks
Custom hooks in React are functions prefixed with "use" that allow you to extract component logic into reusable functions. This approach not only tidies up your components but also promotes reusability and a clean separation of concerns.
Moving Data Fetching Logic to a Custom Hook
Let's walk through the process of extracting the user data-fetching logic from a component and encapsulating it in a custom hook.
Step 1: Create a New Hooks Directory
First, organize your project structure by creating a hooks directory at the top level. This is where we'll store our custom hooks, making them easily accessible across the project.
Step 2: Implementing the useUser Hook
useUser HookWithin the hooks directory, create a new file named user.js. Here, we'll define our custom useUser hook:
// File: hooks/user.js
import { useQuery } from 'react-query';
import { fetchJson } from '../lib/api';
export function useUser() {
const query = useQuery('user', async () => {
try {
return await fetchJson('/api/user');
} catch (err) {
return undefined;
}
}, {
cacheTime: Infinity,
staleTime: 30_000, // ms
});
return query.data;
}In this hook, we're using the useQuery hook from a data-fetching library to fetch and cache user data. The fetchJson function handles the API request, and the query's result is returned as query.data.
Step 3: Refactoring the NavBar Component
With the useUser hook in place, we can simplify components like NavBar by removing redundant code and focusing on rendering logic:
// File: components/NavBar.js
import Link from 'next/link';
import { useUser } from '../hooks/user';
import { fetchJson } from '../lib/api';
function NavBar() {
const user = useUser();
const handleSignOut = async () => {
await fetchJson('/api/logout');
};
return (
<nav>
{user ? (
<span>Signed in as {user.name}</span>
) : (
<span>Not signed in</span>
)}
<button onClick={handleSignOut}>Sign out</button>
</nav>
);
}
export default NavBar;Step 4: Testing and Verifying
After refactoring, it's crucial to test your application to ensure everything works as expected. If the NavBar displays "Signed in as Alice," the refactoring was successful.
Step 5: Reusing Logic Across Components
One of the major benefits of custom hooks is their reusability. Suppose you want to display a special offer message on a ProductPage component:
// File: components/ProductPage.js
import { useUser } from '../hooks/user';
function ProductPage() {
const user = useUser();
return (
<div>
<h1>Product Name</h1>
<p>Price: $100</p>
{user && <p>Only for {user.name}!</p>}
</div>
);
}
export default ProductPage;With the useUser hook, you can effortlessly access user data in any component without duplicating code.
Conclusion
By encapsulating data-fetching logic in custom hooks like useUser, you enhance code reusability and maintainability. This approach keeps components focused on rendering, simplifies state management, and eliminates the need for more complex state management solutions. Embrace custom hooks for a cleaner and more efficient React development experience!
Last updated