I am trying to pass token that comes from Redux store in an axios instance(useRequest). I want to pass the token while I am calling the instance
requestMethod.js
import axios from "axios";
const BASE_URL = "http://localhost:5000/e-mart/";
//Declaring a function to pass the Token dynamically.
export const userRequest =(TOKEN) => axios.create({
baseURL: BASE_URL,
header: { token: `Bearer ${TOKEN}` }, // Here the token comes dynamically
});
Products.js
import { userRequest} from "../requestMethods";
import { useDispatch, useSelector } from "react-redux";
const {accessToken} = useSelector((state) => state.user.currentUser);//accessing token from redux store
useEffect(() => {
const abortController = new AbortController();
const getProdcuts = async () => {
try {
const res = await publicRequest.get(
`products`,
{ signal: abortController.signal } // Here is where i want to pass the token from redux stroe
);
setProducts(res.data);
} catch (err) {
console.log(err.message);
}
};
getProdcuts();
return () => {
abortController.abort();
};
}, []);
If token is dynamic, don't pass it while create axios instance
// Axios instance
export const userRequest =(TOKEN) => axios.create({
baseURL: BASE_URL
})
// how to call with different dynamic tokens
await API.patch('products',
{ signal: abortController.signal },
{
headers: {
token: `Bearer ${TOKEN}`,
},
},
);
Related
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)
I make react app using react router v5, and axios as api instance. I fetch the data in AppRouter file.
Here is my approuter.tsx
const AppRouter = () => {
const dispatch = useAppDispatch();
const token = useAppSelector((state) => state.user.token);
const getUser = useCallback(async () => {
const { data } = await Services.getUser();
dispatch(userAction.setUser(data));
}, [dispatch]);
useEffect(() => {
const localStorage = new LocalStorageWorker();
const storageToken = localStorage.get('token');
dispatch(userAction.setToken(storageToken));
}, [dispatch]);
useEffect(() => {
if (token) {
getUser();
console.log('Worked');
}
}, [token, getUser]);
return (
...
)
}
Actually the function work properly, but I need to refresh the page manually to run these functions. How can I make the function run without refreshing the page?
Update:
The problem is because my axios create instance. I should use interceptors to keep the data fetching in useEffect.
My instance looks like this:
(before update)
const token = localStorage.get('token');
const createInstance = () => {
const instance = axios.create({
baseURL: BASE_URL,
headers: {
'content-type': 'application/json',
Accept: 'application/json',
},
});
instance.defaults.headers.common.Authorization = `Bearer ${token}`;
return instance;
};
(after update)
const createInstance = () => {
const instance = axios.create({
baseURL: BASE_URL,
headers: {
'content-type': 'application/json',
Accept: 'application/json',
},
});
instance.interceptors.request.use(
(config) => {
const token = window.localStorage.getItem('token');
if (token) {
return {
...config,
headers: { Authorization: `Bearer ${token}` },
};
}
return null;
},
(err) => Promise.reject(err)
);
return instance;
};
And now the data fetching is work properly. Thank you
So I architected frontend in the way which encapsulates every API operation tied to a single resource inside custom hook like this:
export default function useSubjects() {
const queryClient: QueryClient = useQueryClient();
const token: string | null = useStore((state) => state.user.token);
const { yearCourseId } = useParams<{ yearCourseId: string }>();
const getSubjects = async () => {
const response = await axios.get(`yearCourses/${yearCourseId}/subjects`, {
headers: { Authorization: `Bearer ${token}` },
});
return response.data;
};
const postSubject = async (subject: SubjectType) => {
const response = await axios.post(`yearCourses/${yearCourseId}/subjects`, subject, {
headers: { Authorization: `Bearer ${token}` },
});
return response.data;
};
const query = useQuery(SUBJECTS_QUERY_KEY, getSubjects);
const postMutation = useMutation(postSubject, {
onSuccess: (subject: SubjectType) => {
queryClient.setQueryData(SUBJECTS_QUERY_KEY, (old: any) => [...old, subject]);
},
});
return { query, postMutation };
}
Now what is the way to globally handle 401 unauthorized? I would like to navigate user to /login on every unauthorized request. Note that I have more hooks like this tied to other resources.
use the onError callback. You can also do this globally as a callback on the queryCache
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: error => {
// check for 401 and redirect here
}
})
})
I am try to fetch users information using github API
import React, { useEffect } from "react";
function UserResults() {
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
const response = await fetch(`${process.env.REACT_APP_GITHUB_URL}/users`, {
headers: {
Authorization: `token ${process.env.REACT_APP_GITHUB_TOKEN}`,
},
});
const data = response.json();
};
return <div>Hello</div>;
}
export default UserResults;
And here is what I put in my env:
REACT_APP_GITHUB_TOKEN="<token>"
REACT_APP_GITHUB_URL = "https://api.github.com"
I am sure the token is correctly generated and copied.
But it seems I can't fetch the data due to some "JSON" error as it shows in the console like this.
Can anyone offers any help with this?
You need to await response.json() and update your header request
import React, { useEffect } from "react";
function UserResults() {
useEffect(() => {
fetchUsers();
}, []);
const fetchUsers = async () => {
const response = await fetch(`${process.env.REACT_APP_GITHUB_URL}/users`, {
headers: {
'Authorization': `token ${process.env.REACT_APP_GITHUB_TOKEN}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
},
});
const data = await response.json();
};
return <div>Hello</div>;
}
export default UserResults;
I would like create async React hook in React-Native for get AsyncStorage data before run my fetch.
Example :
const useCallApi = async url => {
const [instance, token] = await Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
But this hook return an error Unhandled promise rejection. I think the problem is await before useFetch hook, but how i can fix this case ?
If anyone can help me :)
Thank you community,
Why not use AsyncStorage in another way? Only after initialization is complete, you can access AsyncStorage synchronously from anywhere.Through react-native-easy-app, you can operate AsyncStorage like this
import { XStorage } from 'react-native-easy-app';
import { AsyncStorage } from 'react-native';
export const RNStorage = {
token: undefined,
isShow: undefined,
userInfo: undefined
};
const initCallback = () => {
// From now on, you can write or read the variables in RNStorage synchronously
// equal to [console.log(await AsyncStorage.getItem('isShow'))]
console.log(RNStorage.isShow);
// equal to [ await AsyncStorage.setItem('token',TOKEN1343DN23IDD3PJ2DBF3==') ]
RNStorage.token = 'TOKEN1343DN23IDD3PJ2DBF3==';
// equal to [ await AsyncStorage.setItem('userInfo',JSON.stringify({ name:'rufeng', age:30})) ]
RNStorage.userInfo = {name: 'rufeng', age: 30};
};
XStorage.initStorage(RNStorage, AsyncStorage, initCallback);
Maybe adding await before AsyncStorage helps you:
const useCallApi = async url => {
const [instance, token] = await Promise.all([
await AsyncStorage.getItem('instance'),
await AsyncStorage.getItem('token')
]);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
const useCallApi = async url => {
let instance = null;
let token = null;
Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]).then(d=>{
instance = d[0];
token = d[1];
}).catch(e=>throw e);
const data = useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
return data;
};
export default useCallApi;
I think a promise needs then when it resolve and a catch for error catching
You should not implement the hooks like that, it will cause call fetch many times whenever the component which uses this hook re-render.
Try this instead:
const useCallApi = url => {
const [data, setData] = useState(null)
useEffect(() =>{
const fetchData = async () =>{
const [instance, token] = await Promise.all([
AsyncStorage.getItem('instance'),
AsyncStorage.getItem('token')
]);
// I assume that your useFetch is a hook that retruen a Promise of data
const fetchedData = await useFetch(`${instance}/api/v1/${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
});
setData(fetchedData)
}
fetchData()
},[url])
return data;
};