Part 77: Streamlining User Authentication with Custom Hooks in React
[Pages] React Query

In our journey to build a seamless authentication system, we've already developed a useSignIn custom hook to handle user login. This hook efficiently manages the API request and updates the user data in the React Query cache. Our next task is to address the sign-out process, ensuring that the application state updates immediately when a user logs out. Let's explore how to implement this functionality using a custom hook called useSignOut.
Enhancing Code with Constants
Before diving into the sign-out logic, let's make a small but impactful refactoring to improve code clarity and maintainability. Both useSignIn and useUser hooks use the same query key for the user data. By defining a common constant, USER_QUERY_KEY, we ensure consistency and make the code more readable.
// File: hooks/user.js
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { fetchJson } from '../lib/api';
const USER_QUERY_KEY = 'user';Addressing the Sign-Out Issue
Previously, signing out did not update the application state immediately, requiring a page reload to reflect the change. To resolve this, we'll implement a useSignOut hook that uses useMutation to handle the logout request and updates the user data in the cache.
Implementing useSignOut
useSignOutLet's create the useSignOut hook to encapsulate the sign-out logic.
// File: hooks/user.js
export function useSignOut() {
const queryClient = useQueryClient();
const mutation = useMutation(() => fetchJson('/api/logout'));
return async () => {
await mutation.mutateAsync();
queryClient.setQueryData(USER_QUERY_KEY, undefined);
};
}Key Features of useSignOut
useSignOutMutation Setup: Utilizes
useMutationto prepare the logout API request.Cache Update: Sets the user data in the React Query cache to
undefinedafter logout, ensuring the UI updates immediately.
Refactoring the NavBar Component
With useSignOut ready, we can simplify the NavBar component by replacing the old sign-out logic with our new hook.
// File: components/NavBar.js
import Link from 'next/link';
import { useSignOut, useUser } from '../hooks/user';
function NavBar() {
const user = useUser();
const signOut = useSignOut();
console.log('[NavBar] user:', user);
return (
<nav>
<ul>
{user ? (
<>
<li>Welcome, {user.name}</li>
<li>
<button onClick={signOut}>Sign Out</button>
</li>
</>
) : (
<li>
<Link href="/sign-in">Sign In</Link>
</li>
)}
</ul>
</nav>
);
}
export default NavBar;Simplifications in NavBar
Direct Sign-Out Handling: The
signOutfunction fromuseSignOutis directly used as anonClickhandler, eliminating the need for a separate handler function.Immediate UI Update: The NavBar now updates immediately upon sign-out, thanks to the cache update in
useSignOut.
Testing the Implementation
To ensure everything works as expected, follow these steps:
Sign In: Log in using valid credentials and verify that the NavBar updates to reflect the signed-in state.
Sign Out: Click the "Sign Out" button and confirm that the NavBar updates immediately to show the signed-out state.
Network Requests: Use browser developer tools to verify that the appropriate network requests are made and that no unnecessary requests occur after signing out.
Conclusion
By creating a useSignOut custom hook, we've effectively addressed the sign-out issue, ensuring that the application state updates promptly when a user logs out. This approach not only improves the user experience but also keeps our codebase organized and maintainable. As you continue building applications, consider leveraging custom hooks to encapsulate common logic, making your React components simpler and more focused.
Last updated