I have this scenario that is after the user login and assuming it is success, user details / user token is stored to localStorage
and will automatically navigate to dashboard page, dashboard page has some api calls and those api calls required/needs token
that is stored in the localStorage
, my problem is that it is unable to retrieve those values in localStorage
, but when I check from localStorage
using console, the key/value is there, I noticed that, I need to refresh the page to retrieve those details without a problem. How can I possibly fix this issue? to be able to get localStorage
value after navigating to another component?
Here is my code for index.tsx
ReactDOM.render(
<AuthContextProvider>
<App />
</AuthContextProvider>,
document.getElementById("root")
);
AuthContext code:
const AuthContext = React.createContext({
user: "",
isLoggedIn: false,
login: (userdata: any, expirationTime: string) => {},
logout: () => {},
});
export const AuthContextProvider = (props: any) => {
const initialUser = localStorage.getItem("user") || "";
const [userData, setUserData] = useState(initialUser);
const userIsLoggedIn = !!userData;
const logoutHandler = () => {
setUserData("");
localStorage.removeItem("user");
};
const loginHandler = async (
user: any,
expirationTime: string
) => {
localStorage.setItem("user", JSON.stringify(user));
setUserData(user);
};
const contextValue = {
user: userData,
isLoggedIn: userIsLoggedIn,
login: loginHandler,
logout: logoutHandler,
};
return (
<AuthContext.Provider value={contextValue}>
{props.children}
</AuthContext.Provider>
);
};
export default AuthContext;
App.tsx code
function App() {
const authCtx = useContext(AuthContext);
return (
<BrowserRouter>
<Routes>
{!authCtx.isLoggedIn && (
<Route element={<LoginLayout />}>
<Route index element={<SignInForm />} />
{/*Other links here */}
</Route>
)}
{authCtx.isLoggedIn && (
<Route element={<AdminLayout />}>
<Route path="dashboard" element={<DashboardScreen />} />
{/*Other links here */}
</Route>
)}
<Route path="*" element={<PageNotFound />} />
</Routes>
</BrowserRouter>
);
}
Login code:
try {
await AuthService.login(email, password).then(
(res) => {
authCtx.login(res, "0");
navigate("../dashboard", { replace: true });
},
(error) => {
}
);
} catch (err) {
console.log(err);
}
Dashboard code:
const loadCountOnlineUsers = useCallback(async () => {
try {
await DashboardService.loadCountOnlineUsers().then(
(res) => {
setCntOnlineUsers(res.count);
setflagOnlineUsers(false);
},
(error) => {
setflagOnlineUsers(false);
}
);
} catch (err) {
console.log(err);
setflagOnlineUsers(false);
}
}, [setCntOnlineUsers, setflagOnlineUsers]);
useEffect(() => {
loadCountOnlineUsers();
}, [loadCountOnlineUsers]);
Dashboard service code:
const config = {
headers: {
"Content-Type": "application/json",
Authorization: AuthHeader(),
},
params: {},
};
const loadCountOnlineUsers = () => {
config["params"] = {};
return axios
.get(API_URL "api/v1/dashboard-related/online-users", config)
.then((response) => {
return response.data;
});
};
const DashboardService = {
loadCountOnlineUsers,
};
export default DashboardService;
Auth-header code:
export default function AuthHeader() {
const user = JSON.parse(localStorage.getItem("user") || "{}");
if (user && user.token) {
return "Bearer " user.token;
} else {
return "";
}
}
CodePudding user response:
The problem is that the check to localStorage in AuthHeader() isn't updating reactively. The fix would be to rewrite AuthHeader to accept the user data like this:
export default function AuthHeader(user) {
const user = JSON.parse(user || "{}");
if (user && user.token) {
return "Bearer " user.token;
} else {
return "";
}
}
and then continue the data piping into the area where AuthHeader() is called, perhaps like this:
const config = (user) => ({
headers: {
"Content-Type": "application/json",
Authorization: AuthHeader(),
},
params: {},
});
const loadCountOnlineUsers = (user) => {
config["params"] = {};
return axios
.get(API_URL "api/v1/dashboard-related/online-users", config(user))
.then((response) => {
return response.data;
});
};
const DashboardService = {
loadCountOnlineUsers,
};
Lastly, using an effect in the dashboard to update it reactively, while connecting to context:
const authCtx = useContext(AuthContext);
const user = authCtx.user;
const loadCountOnlineUsers = (user) => {
return useCallback(async () => {
try {
await DashboardService.loadCountOnlineUsers(user).then(
(res) => {
setCntOnlineUsers(res.count);
setflagOnlineUsers(false);
},
(error) => {
setflagOnlineUsers(false);
}
);
} catch (err) {
console.log(err);
setflagOnlineUsers(false);
}
}, [setCntOnlineUsers, setflagOnlineUsers]);
}
useEffect(() => {
loadCountOnlineUsers(user);
}, [loadCountOnlineUsers, user]);