useAuth Hook in React – DEV Community

useAuth Hook in React – DEV Community


Image description

Authentication and authorization are crucial aspects of web applications. Instead of handling them manually in multiple components, we can create a reusable hook to simplify the logic. In this article, we’ll first implement authentication logic inside a component and then refactor it using a custom useAuth hook.



Authentication logic without a custom hook

Let’s start by creating a React component that manages user authentication manually.

import React, { useState } from "react";

const AuthComponent = () => {
  const [user, setUser] = useState(null);

  const login = (username) => {
    setUser({ name: username, role: "user" });
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <div>
      {user ? (
        <>
          <h2>Welcome, {user.name}!h2>
          <button onClick={logout}>Logoutbutton>
        >
      ) : (
        <button onClick={() => login("John Doe")}>Loginbutton>
      )}
    div>
  );
};

export default AuthComponent;
Enter fullscreen mode

Exit fullscreen mode



Issues with this approach

  • Authentication logic is tied to a single component, making it hard to reuse.
  • No centralized way to manage user state across the app.
  • Authorization checks need to be manually repeated in multiple components.



Creating a Custom useAuth Hook

To make authentication reusable, we’ll create a useAuth hook that manages login, logout, and user state globally.



AuthProvider (Context Provider)

import { useState, createContext } from "react";

export const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(() => {
    const storedUser = localStorage.getItem("user");
    return storedUser ? JSON.parse(storedUser) : null;
  });

  const login = (username, role) => {
    const userData = { name: username, role };
    setUser(userData);
    localStorage.setItem("user", JSON.stringify(userData));
  };

  const logout = () => {
    setUser(null);
    localStorage.removeItem("user");
  };

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    AuthContext.Provider>
  );
};
Enter fullscreen mode

Exit fullscreen mode



useAuth Hook

import { useContext } from "react";
import { AuthContext } from "./AuthProvider";

export const useAuth = () => {
  const { user, login, logout } = useContext(AuthContext);

  const isAuthorized = (requiredRole) => {
    return user && user.role === requiredRole;
  };

  return { user, login, logout, isAuthorized };
};
Enter fullscreen mode

Exit fullscreen mode



Advantages

✅ Can be used across multiple components.
✅ Provides a centralized authentication state.
✅ Eliminates redundant authentication logic.



Using useAuth in a Component

Let’s refactor our authentication component to use the useAuth hook.

import React from "react";
import { useAuth } from "./useAuth";

const AuthComponent = () => {
  const { user, login, logout } = useAuth();

  return (
    <div>
      {user ? (
        <>
          <h2>Welcome, {user.name}!h2>
          <button onClick={logout}>Logoutbutton>
        >
      ) : (
        <button onClick={() => login("John Doe", "user")}>Loginbutton>
      )}
    div>
  );
};

export default AuthComponent;
Enter fullscreen mode

Exit fullscreen mode



Wrapping the Root Component with AuthProvider

To make authentication available across the app, wrap your root component with AuthProvider.

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { AuthProvider } from "./AuthProvider";
import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <AuthProvider>
      <App />
    AuthProvider>
  StrictMode>
);
Enter fullscreen mode

Exit fullscreen mode



Example: Protecting Admin Routes

import React from "react";
import { useAuth } from "./useAuth";

const AdminPanel = () => {
  const { user, isAuthorized } = useAuth();

  if (!user || !isAuthorized("admin")) {
    return <h2>Access Deniedh2>;
  }

  return <h2>Welcome to the Admin Panelh2>;
};

export default AdminPanel;
Enter fullscreen mode

Exit fullscreen mode



Example: Protecting Routes with Higher-Order Component (HOC)

import React from "react";
import { useAuth } from "./useAuth";
import { Navigate } from "react-router-dom";

const ProtectedRoute = ({ children, requiredRole }) => {
  const { user, isAuthorized } = useAuth();

  if (!user || (requiredRole && !isAuthorized(requiredRole))) {
    return <Navigate to="/login" replace />;
  }

  return children;
};

export default ProtectedRoute;
Enter fullscreen mode

Exit fullscreen mode



Advantages

✅ Simplifies role-based access control.
✅ Avoids repeating authorization checks.
✅ Easily protect routes with a reusable ProtectedRoute component.



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *