Logo

dev-resources.site

for different kinds of informations.

Managing Auth State in react using useContext API

Published at
9/8/2024
Categories
react
auth
webdev
javascript
Author
mmvergara
Categories
4 categories in total
react
open
auth
open
webdev
open
javascript
open
Author
9 person written this
mmvergara
open
Managing Auth State in react using useContext API

This code snippet is all you need to manage auth state on your react application uses the Context API to manage user state across the application.

no more babbling, let's just dive into it.

Imports and Type Definitions

import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useEffect } from "react";
import { useLocalStorage } from "../utils/useLocalStorage";

type SignInForm = { email: string; password: string; };
type User = { id: number; email: string; };
type AuthState = User & { exp: number };
type UserContextType = {
  user: User | null;
  setUser: Dispatch<SetStateAction<AuthState | null>>;
  signOut: () => Promise<string | undefined>;
  signIn: (signInForm: SignInForm) => Promise<string | undefined>;
};
Enter fullscreen mode Exit fullscreen mode

We start by importing necessary React hooks and a custom useLocalStorage hook. Then, we define TypeScript types for our authentication system, including SignInForm, User, AuthState, and UserContextType.

Creating the Context and Custom Hook

const AuthDataContext = createContext<UserContextType | undefined>(undefined);

export const useAuth = (): UserContextType => {
  const context = useContext(AuthDataContext);
  if (!context) {
    throw new Error("useAuth must be used within a UserDataProvider");
  }
  return context;
};
Enter fullscreen mode Exit fullscreen mode

Here, we create the AuthDataContext and a custom useAuth hook. This hook ensures that we're using the context within a provider and provides a convenient way to access our auth state.

AuthProvider Component

export const AuthProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useLocalStorage<AuthState | null>("user", null);

  // ... (other functions)

  return (
    <AuthDataContext.Provider value={{ user, setUser, signIn, signOut }}>
      {children}
    </AuthDataContext.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

The AuthProvider component is the core of our auth system. It uses the useLocalStorage hook to persist the user state and provides the context value to its children.

JWT Expiration Check

const isJwtExpired = (unixTime: number) => {
  const currentTime = Math.floor(Date.now() / 1000);
  const timeRemaining = unixTime - currentTime;
  if (timeRemaining <= 0) {
    console.log("The JWT is expired.");
    setUser(null);
    return true;
  } else {
    const hours = Math.floor(timeRemaining / 3600);
    const minutes = Math.floor((timeRemaining % 3600) / 60);
    console.log(`Time remaining before JWT expires: ${hours} hours ${minutes} minutes`);
    return false;
  }
};
Enter fullscreen mode Exit fullscreen mode

This function checks if the JWT has expired and logs the remaining time if it's still valid.

Sign Out Function

const signOut = async () => {
  const res = await fetch("http://localhost:8080/auth/signout", { method: "POST" });
  setUser(null);
  if (!res.ok) {
    console.log("Error signing out");
    return (await res.text()) || "Something went wrong";
  }
};
Enter fullscreen mode Exit fullscreen mode

The signOut function makes a POST request to the signout endpoint and clears the user state.

Sign In Function

const signIn = async (signInForm: SignInForm) => {
  const res = await fetch("http://localhost:8080/auth/signin", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(signInForm),
  });
  if (!res.ok) {
    return (await res.text()) || "Something went wrong";
  }
  const data = (await res.json()) as { user: User; exp: number };
  if (data) {
    setUser({ ...data.user, exp: data.exp });
  }
};
Enter fullscreen mode Exit fullscreen mode

The signIn function sends the user's credentials to the signin endpoint and updates the user state with the response data.

useEffect for JWT Expiration Check

useEffect(() => {
  if (!user) return;
  if (isJwtExpired(user.exp)) signOut();
}, [user]);
Enter fullscreen mode Exit fullscreen mode

This effect runs whenever the user state changes, checking if the JWT has expired and signing out if necessary.

Here's an example implementation of the useLocalStorage hook btw

import { useState, useEffect, Dispatch, SetStateAction } from "react";

export function useLocalStorage<T>(
  key: string,
  initialValue: T
): [T, Dispatch<SetStateAction<T>>] {
  const [storedValue, setStoredValue] = useState<T>(() => {
    try {
      const item = localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  const setValue: Dispatch<SetStateAction<T>> = (value) => {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    const handleStorageChange = (event: StorageEvent) => {
      if (event.key === key) {
        setStoredValue(JSON.parse(event.newValue || "null"));
      }
    };

    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, [key]);

  return [storedValue, setValue];
}
Enter fullscreen mode Exit fullscreen mode

and you're done? easy peasy lemon squeezy.. make sure to modify the fetching logic for your own api structure if needed.

auth Article's
30 articles in total
Favicon
Wait, are we just handing over system access to the AI agents?
Favicon
Implementing Auth in .NET WebApi & SPAs: Why is it still so painful?
Favicon
Secure Your Nuxt 3 App
Favicon
Managing Auth State in react using useContext API
Favicon
How to Authenticate Users Codeigniter Shield
Favicon
How to decode a JWT
Favicon
Laravel 11 API Rest Auth with jwt-auth
Favicon
Announcement - Keycloak.AuthServices v2.0.0 is out 🎉!
Favicon
Generate magic tokens in Rails with generates_token_for
Favicon
Your organization has enabled or enforced SAML SSO ... you must re-authorize the OAuth Application `GitHub for VS Code`
Favicon
JWT Revokation
Favicon
Recent Security Vulnerability Detected in Clerk - Should You Roll Your Own Auth?
Favicon
User Management Unveiled: An Architectural Overview
Favicon
Compressing and Decompressing User Permissions with JavaScript
Favicon
Clerk Webhooks: Data Sync with Convex
Favicon
Simplifying Client-Side Authentication with Firebase and SvelteKit
Favicon
I Just Want Authentication To Work
Favicon
Setup User Auth for your Reflex app using local_auth
Favicon
How to Implement Passkey Authentication and Fine-Grained Authorization in JavaScript
Favicon
Authentication Workflows Overview
Favicon
Securing MQTT: A Guide to Basic Authentication
Favicon
Shopify Passkey Implementation Analyzed
Favicon
Apa itu Autentikasi: Definisi dan Jenis-jenis Autentikasi
Favicon
Best Practices for Authorization in Microservices
Favicon
Granular Permission Management with CASL Library
Favicon
Multi Auth System in Laravel Breeze #1
Favicon
Simplifying Authentication Integration For Developers With Authgear SDKs
Favicon
API Authentication Methods - Pros and Cons
Favicon
Authentication vs. Authorization
Favicon
Twitter API suspended? Here's how to fix it

Featured ones: