So I have created these contexts to handle logging users in and retrieving the logged user to any component that might need it.
Here they are:
context.js
import React, { useReducer } from "react";
import { AuthReducer, initialState } from "./reducers";
const AuthStateContext = React.createContext();
const AuthDispatchContext = React.createContext();
export function useAuthState() {
const context = React.useContext(AuthStateContext);
if (context === undefined) {
throw new Error("useAuthState must be used within a AuthProvider");
}
return context;
}
export function useAuthDispatch() {
const context = React.useContext(AuthDispatchContext);
if (context === undefined) {
throw new Error("useAuthDispatch must be used within a AuthProvider");
}
return context;
}
export const AuthProvider = ({ children }) => {
const [user, dispatch] = useReducer(AuthReducer, initialState);
return (
<AuthStateContext.Provider value={user}>
<AuthDispatchContext.Provider value={dispatch}>
{children}
</AuthDispatchContext.Provider>
</AuthStateContext.Provider>
);
}
reducers.js
let user = localStorage.getItem("currentUser")
? JSON.parse(localStorage.getItem("currentUser")).user
: "";
let token = localStorage.getItem("currentUser")
? JSON.parse(localStorage.getItem("currentUser")).token
: "";
export const initialState = {
userDetails: user || "",
token: token || "",
loading: false,
errorMessage: null,
};
export const AuthReducer = (initialState, action) => {
switch (action.type) {
case "REQUEST_LOGIN":
return {
...initialState,
loading: true,
};
case "LOGIN_SUCCESS":
return {
...initialState,
userDetails: action.payload.user,
token: action.payload.token,
loading: false,
};
case "LOGOUT":
return {
...initialState,
userDetails: "",
token: "",
};
case "LOGIN_ERROR":
return {
...initialState,
loading: false,
errorMessage: action.error,
};
default:
throw new Error(`Unhandled action type: ${action.type}`);
}
};
actions.js
const ROOT_URL = process.env.REACT_APP_API_HOST_URL;
export async function loginUser(dispatch, loginPayload) {
const requestOptions = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(loginPayload),
};
try {
dispatch({ type: "REQUEST_LOGIN" });
let response = await fetch(`${ROOT_URL}/auth/login`, requestOptions);
let data = await response.json();
if (data.user) {
dispatch({ type: "LOGIN_SUCCESS", payload: data });
localStorage.setItem("currentUser", JSON.stringify(data));
return data;
}
dispatch({ type: "LOGIN_ERROR", error: data.errors[0] });
return;
} catch (error) {
dispatch({ type: "LOGIN_ERROR", error: error });
}
}
export async function logout(dispatch) {
dispatch({ type: "LOGOUT" });
localStorage.removeItem("currentUser");
localStorage.removeItem("token");
}
my question is how to expand this to check whether the JWT has expired or not every time the useAuthState() hook is called (if this is even the best way to go about things)? and then log the user out or perhaps refresh the token from the server without having to log the user out if possible.
Thanks in advance.