Axios making 2 requests on refresh - reactjs

When I navigate using Link (react router-dom) I don't have this problem, but if I refresh the browser I get a 403 error in console saying unauthorised and then I get the data in the next request with a 200 response. Why is this making what looks like 2 requests when refreshing the browser?
import { AuthContext } from "../../shared/context/auth-context";
const ContactEntries = () => {
const auth = useContext(AuthContext);
useEffect(() => {
const source = Axios.CancelToken.source();
setIsLoading(true);
const getContactEnquiries = async () => {
try {
const response = await Axios.get(
`${process.env.REACT_APP_BACKEND_URL}/v1/contact`,
{
cancelToken: source.token,
headers: { Authorization: "Bearer " + auth.token },
}
);
if (response.status === 200) {
setIsLoading(false);
setEnquiries(response.data.enquiries);
}
} catch (err) {
setIsLoading(false);
console.log(err.response);
}
};
getContactEnquiries();
return () => {
source.cancel();
};
}, [!!auth.token]);
}
Here is my authContext:
import { createContext } from "react";
export const AuthContext = createContext({
isLoggedIn: false,
userId: null,
token: null,
email: null,
firstName: null,
login: () => {},
logout: () => {},
});

This is because your useEffect is running twice on refresh. On first render it is not getting auth.token and may be it null. And on second render it is making call with 200 status code.
You have to check auth token it coming successfully.
You can check it this way
useEffect(() => {
const source = Axios.CancelToken.source();
setIsLoading(true);
const getContactEnquiries = async () => {
try {
const response = await Axios.get(
`${process.env.REACT_APP_BACKEND_URL}/v1/contact`,
{
cancelToken: source.token,
headers: { Authorization: "Bearer " + auth.token },
}
);
if (response.status === 200) {
setIsLoading(false);
setEnquiries(response.data.enquiries);
}
} catch (err) {
setIsLoading(false);
console.log(err.response);
}
};
if(auth.token) getContactEnquiries();
return () => {
source.cancel();
};
}, [!!auth.token]);

Related

ReactJs how to add interceptor in axios

I've been working on this for hours, and I have no idea where did it go wrong.
I want to have an axios interceptor for my ReactJs
this is my interceptor axiosHandler.js
import axios from "axios";
const axiosHandler = axios.create({
baseURL: process.env.REACT_APP_BASE_URL,
headers: {
Accept: "application/json",
},
});
axiosHandler.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = "Bearer " + token;
}
return config;
},
(error) => {
Promise.reject(error);
}
);
//axiosHandler.interceptors.response
export default axiosHandler;
And here is how I use the handler in my other component
import axiosHandler from "../services/axiosHandler";
const getData = async () => {
await axiosHandler
.get(`/path`)
.then((response) => {
//do something
})
};
And I get an error of below
services_axiosHandler__WEBPACK_IMPORTED_MODULE_0_.get is not a function
I've read many other solutions, but I can't find the difference as how it leads to the error of mine.
Where do I put it wrong?
Thank you
inside axios.index
import axios from "axios";
import { API_URL } from "../config/config";
const axiosHttp = axios.create({
baseURL: `${API_URL}`,
});
axiosHttp.interceptors.request.use(
(config) => {
const token = "Your Token here"
return {
...config,
headers: {
...(token !== null && { Authorization: `${token}` }),
...config.headers,
},
};
},
(error) => {
return Promise.reject(error);
}
);
axiosHttp.interceptors.response.use(
(response) => {
//const url = response.config.url;
//setLocalStorageToken(token);
return response;
},
(error) => {
if (error.response.status === 401) {
//(`unauthorized :)`);
//localStorage.removeItem("persist:root");
//removeLocalStorageToken
//window.location.href = "/login";
}
return Promise.reject(error);
}
);
export default axiosHttp;
Then inside your API function use it like below
import axiosHttp from "./utils/axios";
const getData = async ()=>{
try{
const response = await axiosHttp.get('/path')
return resposne;
}
catch(error){
//handle error here...
}
}
Last but not least, you shouldn't use await when using callback (then/catch)

How to programatically call an api in react js

I'm using react js with fetch ,I wanna call an api of refresh token Before my token be expired ,then I calculate the difference between my local date and expire date of the token . I want to call my refresh token api every (expire - localData) - 5 min and update my token in my localStorage.
Is that possible ?
A call my refresh token in Api.js to work globally ,Is that right ?
This is my code but doesn't work like excepting:
Api.js
const duration = localStorage.getItem("duration")
useEffect(() =>{
if(token)
setInterval(() => {
RefreshApi.getRefreshToken().then(res => {
if (typeof res === "string") {
console.log("Error",res)
} else {
var now = moment(new Date(), "DD/MM/YYYY HH:mm:ss");
var expire = moment(new Date(res.expire), "DD/MM/YYYY HH:mm:ss");
var diffTime = moment.duration(expire.diff(now));
localStorage.setItem("token",res.token);
localStorage.setItem("expireDuration",diffTime.asMilliseconds());
localStorage.setItem("expire",res.expire);
}
}).catch(err => {
console.log(err);
})
},(duration - 300000))
},[])
refreshApi.js
getRefreshToken: function () {
return api.get("/refreshToken",{"Authorization": `Bearer ${localStorage.getItem("token")}`}).then(res => {
if (res.ok) {
return res.json();
} else {
return res.text();
}
});
},
Any help please ?
You can do this in different ways.
You can use the fetch API provided by JavaScript
SomeJsxFile.jsx
import { getRefreshToken } from './RefreshToken'
const refreshApi = async () => {
const token = localStorage.getItem("token");
if(token) {
const response = await getRefreshToken(token)
const now = Date.now() // this will return the Epoch time in milliseconds
const expire = Date.now(response.expire)
localStorage.setItem("token", response.token);
localStorage.setItem("expireDuration", now - expire);
localStorage.setItem("expire", response.expire);
return
}
// if dont have the token do something else
}
useEffect(() => {
// 10min = 600000 milleseconds
setInterval(refreshApi(), 600000)
},[])
RefreshToken.js
module.exports = () => ({
getRefreshToken: async (token) => {
return new Promise((resolve, reject) => {
fetch("https://localhost:3000/refreshToken", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`
},
cache: "default"
})
.then(response => response.json())
.then(responseInJSON => resolve(responseInJSON))
.catch(err => reject(err))
})
}
})
You can use a third-party library, one that is used a lot is the axios.js, you can follow this example:
Api.js
import axios from 'axios'
const api = axios.create({ baseURL: 'http://localhos:3000' })
module.exports = {
api
}
RefreshToken.js
import { api } from './Api'
module.exports = () => ({
getRefreshToken: async (token) => {
return api.get('/refreshToken', {
headers: {
"Authorization": `Bearer ${token}`
}
})
}
})

Remove item from screen after delete

I had this working with react app but I am porting the project to next.js and it doesn't work for some reason. I am using SWR
Custom hook:
import axios from "axios";
import useSwr from "swr";
export const useHttp = (url, token) => {
const fetcher = (url, token) =>
axios
.get(url, { headers: { Authorization: "Bearer " + token } })
.then((res) => res.data);
const { data, error } = useSwr(token ? [url, token] : null, fetcher);
return {
data: data,
isLoading: !error && !data,
isError: error,
};
};
In my page
const { data, isError, isLoading } = useHttp(
`http://localhost:8000/api/admin/bookings/${id}`,
session.user.servertoken
);
const deleteBooking = async (id) => {
//immediatly remove record from the screen before getting response from the server
mutate(
[
`http://localhost:8000/api/admin/bookings/${id}`,
session.user.servertoken,
],
data.filter((d) => d._id !== id),
false
);
try {
const response = await axios.delete(
`${process.env.NEXT_PUBLIC_API_ENDPOINT}/admin/bookings/${id}`,
{ headers: { Authorization: "Bearer " + session.user.servertoken } }
);
mutate([
`http://localhost:8000/api/admin/bookings/${id}`,
session.user.servertoken,
]);
} catch (err) {
console.log(err);
}
};
The button:
<Button
variant="danger"
onClick={() => deleteBooking(d._id)}
>
Delete
</Button>
Absolutely nothing happens in terms of the ui though (record IS deleted in database). The record should disappear from the screen once I click on delete and the deleteBooking function runs.
*If I add this to my deleteBooking function it shows the correct data:
const filteredData = data.filter((d) => d._id !== id);

Values won't update after api call with axios

I'm trying to fetch data with axios using React's useEffect in two steps:
1- Get the access token with a POST request
2- Use the token on another POST request to get the desired data
After the first post request, the response returns as expected, but the state value doesn't update. So it sends undefined as the token for the second request.
const [infos, setInfos] = useState(null)
const [token, setToken] = useState('')
useEffect(() => {
const getToken = async () => {
try {
const response = await axios.post(
'adress',
{
username: 'root',
password: 'i_want_my_token',
}
)
setToken(response.data.access)
} catch (error) {
console.log(error)
}
}
getToken()
const getCatalogo = async () => {
try {
let data = { id: 6 }
let configCatalogo = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}
const catalogoResponse = await axios
.post(
'adress',
data,
configCatalogo
)
setInfos(res.data)
} catch (error) {
console.log(error) }
}
getCatalogo()
}, [])
Since the function to get/set your token is asynchronous, you need to use two different useEffects: one to fetch the token and one to use that token once it is set.
const [infos, setInfos] = useState(null)
const [token, setToken] = useState('')
useEffect(() => {
const getToken = async () => {
try {
const response = await axios.post(
'adress',
{
username: 'root',
password: 'i_want_my_token',
}
)
setToken(response.data.access)
} catch (error) {
console.log(error)
}
}
getToken()
}, []);
useEffect(() => {
const getCatalogo = async () => {
try {
let data = { id: 6 }
let configCatalogo = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
}
const catalogoResponse = await axios
.post(
'adress',
data,
configCatalogo
)
setInfos(res.data)
} catch (error) {
console.log(error) }
}
getCatalogo()
}, [token]);

Gets 401 error while user tries to do Fetch Get request after authentication

I'm trying to get user details after user looged in but user is getting 401 error even user is looged in with 200 ok.
Explanation of process:
i have logged in user using fetch post request.
stored username,role,staffid to async storage
now i want to list all user (with /api/staff endpoint response throws user firstname and last name )with fetch get request but whenever i make GET request it
throws 401 error.
It will be lifesaver to crack this step for me,thank you!
here is my code
import AsyncStorage from "#react-native-community/async-storage";
import React, { useState, useEffect } from "react";
import { SafeAreaView, Text, StyleSheet, Alert } from "react-native";
import AuthService from "../api/auth-service";
import BASE_URL from "../api/baseUrl";
export default function HomeScreen(props) {
const [firstName, setFirstName] = useState({});
const [lastName, setLastName] = useState({});
const [userValue, setUserValue] = useState({});
useEffect(() => {
let mounted = true;
if (mounted) {
getDataFromStorage();
getUserInfo();
}
return () => {
mounted = false;
};
}, []);
const getDataFromStorage = async () => {
let user = await AsyncStorage.getItem("LoggedInUser");
setUserValue(JSON.parse(user));
};
const getUserInfo=async()=>{
return fetch(BASE_URL+"/api/staff")
.then((response) => {
if(response.ok){
console.log(response);
}else{
console.log(response.status);
}
})
.catch((error) => {
console.log(error);
this.setState({ errorMsg: "Error retreiving data" });
});
}
return (
<SafeAreaView>
<Text>
{"Good morning " + userValue.username + " "}
{"you role is " + userValue.role +"your staff id is " + userValue.staffId+" " + "your first name is "+ firstName +"this is your last name"+lastName}
</Text>
</SafeAreaView>
);
}
authservice.js
import AsyncStorage from "#react-native-community/async-storage";
import BASE_URL from "./baseUrl";
class AuthService {
login(Username, Password, role) {
console.log(Username, role);
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
});
}
logout() {
AsyncStorage.getAllKeys().then((keys) => AsyncStorage.multiRemove(keys));
}
}
export default new AuthService();
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
(data) => {
console.log(JSON.stringify(data));
AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
According to developer.mozilla.org
The HTTP 401 Unauthorized client error status response code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
It's seems the user doesn't have right to access the API. Make sure that the getUserInfo() API, /api/staff, don't need any authentication token in header of your HTTP request.
I have a feeling that you may need to resolve one more promise in AuthService.login.
res.json() is actually a promise which needs to be resolved as well, so you may need one more then block like so:
return fetch(BASE_URL + "/api/authentication/login", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify({
Username,
Password,
}),
}).then((res) => {
if (res.ok) {
console.log("the login response", res);
return res.json();
} else {
alert("Invalid Username or Password");
window.stop();
}
}).then(finalData=>finalData )// <---------- add this
.catch(err=> err)
It happens because getDataFromStorage is an async function so you have to resolve it first. Also in login.js we have to await before navigate to make sure that the data is saved in AsyncStorage. Please update the following part of your code:
login.js
const submitData = async () => {
AuthService.login(Username, Password).then(
async (data) => {
console.log(JSON.stringify(data));
await AsyncStorage.setItem("LoggedInUser", JSON.stringify(data));
if (data.role == "Admin") {
console.log(data.username);
navigation.navigate("adminPage");
} else {
navigation.navigate("staffpage");
}
},
(error) => {
Alert.alert(error);
}
);
};
next, initialize userValue with null
const [userValue, setUserValue] = useState(null);
Now have 2 useEffect,
useEffect(() => {
let mounted = true; //Why this required as it doesn't mean anything
if (!userValue) {
getDataFromStorage();
}
return () => {
mounted = false;
};
}, []);
useEffect(()=>{
if(userValue){
getUserInfo()
}
},[userValue])

Resources