Goal
My goal is to call componentDidMount() function to return some values from another method called getUserPlaylists().
Problem
The problem I am encountering is that the componentDidMount() shows me value of undefined and getUserPlaylists() shows me a result of an array.
Actual result
Code
Within Spotify.js file I have the following code:
const clientId = 'Cleint ID Here';
const redirectUri = 'http://localhost:3000/';
let accessToken;
let userId;
const Spotify = {
getAccessToken() {
if (accessToken) {
return accessToken;
}
const accessTokenMatch = window.location.href.match(/access_token=([^&]*)/);
const expiryInMatch = window.location.href.match(/expires_in=([^&]*)/);
if (accessTokenMatch && expiryInMatch) {
accessToken = accessTokenMatch[1];
const expiresIn = Number(expiryInMatch[1]);
window.setTimeout(() => accessToken = '', expiresIn * 10000);
window.history.pushState('Access Token', null, '/');
return accessToken;
} else {
const accessUrl = `https://accounts.spotify.com/authorize?client_id=${clientId}&response_type=token&scope=playlist-modify-public&redirect_uri=${redirectUri}`;
window.location = accessUrl;
}
},
async getUserPlaylists() {
await Spotify.getCurrentUserId().then(userId => {
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
fetch(` https://api.spotify.com/v1/users/${userId}/playlists`, {
headers : headers
})
.then(res => res.json())
.then(res => {
if(!res.items) {
return [];
} else {
console.log(res.items)
return res.items;
}
})
})
},
getCurrentUserId() {
if (userId) {
return new Promise((resolve) => {
resolve(userId);
})
} else {
return new Promise((resolve) => {
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
return fetch("https://api.spotify.com/v1/me", { headers: headers })
.then(res => res.json())
.then(jsonRes => {
userId = jsonRes.id;
resolve(userId);
});
})
}
}
}
export { Spotify };
Summary
I have 3 objects that can be called as methods within my app.js file.
Here is how I call the componentDidMount() within my app.js file:
async componentDidMount() {
const val = await Spotify.getUserPlaylists();
console.log(val)
}
Expected result
The componentDidMount() should return the same value as getUserPlaylists()
Question
I don't understand why componentDidMount() is returning value of undefined?
Cause you're not returning anything from getUserPlaylists
async getUserPlaylists() {
// here return missed
return await Spotify.getCurrentUserId().then(userId => {
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
// here return too
return fetch(` https://api.spotify.com/v1/users/${userId}/playlists`, {
headers : headers
})
.then(res => res.json())
.then(res => {
if(!res.items) {
return [];
} else {
console.log(res.items)
return res.items.map(playlist => ({
playlistId: playlist.id,
playListName: playlist.name
}));
}
})
})
},
You can simply use the below code, which does the same
async getUserPlaylists() {
// here return missed
try {
const userId = await Spotify.getCurrentUserId()
const accessToken = Spotify.getAccessToken();
const headers = { Authorization: `Bearer ${accessToken}` };
// here return too
const result = await fetch(` https://api.spotify.com/v1/users/${userId}/playlists`, { headers })
const res = await result.json()
if(!res.items) return [];
console.log(res.items)
return res.items.map(playlist => ({ playlistId: playlist.id, playListName: playlist.name }));
} catch(err) {
console.log({ err })
}
}
Related
I am having issues with setting state. When I try to set the state of setDataTemp() from axios the data sent into setDataTemp is blank. however if I just console log the data directly I get response. Not sure what wrong
setDataTemp is not empty
const [dataTemp, setDataTemp] = useState([]);
const _overlayFilder = async () => {
let source = axios.CancelToken.source();
await axios
.get(network + '/getOverlayList', {
cancelToken: source.token,
})
.then(response => {
removeRootUUID(response.data.items, response.data.items);
// console.log(response.data);
return response.data;
})
.then(response => {
setDataTemp(response.items);
})
.catch(function (e) {
if (axios.isCancel(e)) {
console.log(`request cancelled:${e.message}`);
} else {
console.log('another error happened:' + e.message);
}
})
.finally(() => {
console.log(dataTemp)
});
If I don't set the state setDataTemp and I just console.log instead I prints the data
const [dataTemp, setDataTemp] = useState([]);
const _overlayFilder = async () => {
let source = axios.CancelToken.source();
await axios
.get(network + '/getOverlayList', {
cancelToken: source.token,
})
.then(response => {
removeRootUUID(response.data.items, response.data.items);
// console.log(response.data);
return response.data;
})
.then(response => {
// the data that I want to set into setDataTemp
console.log(response.items);
})
.catch(function (e) {
if (axios.isCancel(e)) {
console.log(`request cancelled:${e.message}`);
} else {
console.log('another error happened:' + e.message);
}
})
You are mixing an async function handling using a Promise-like approach with your axios call.
Try to change your code like this:
const [dataTemp, setDataTemp] = useState([]);
const _overlayFilder = async () => {
try {
let source = axios.CancelToken.source();
const { data } = await axios.get(network + '/getOverlayList', {
cancelToken: source.token,
});
removeRootUUID(data.items, data.items);
setDataTemp(data.items);
console.log(dataTemp);
} catch (err) {
if (axios.isCancel(err)) {
console.log(`request cancelled:${e.message}`);
} else {
console.log('another error happened:' + e.message);
}
}
};
i want this function to return either true or false, instead I get
/**
* Sends request to the backend to check if jwt is valid
* #returns {boolean}
*/
const isAuthenticated = () => {
const token = localStorage.getItem('jwt');
if(!token) return false;
const config = {headers : {'x-auth-token' : token}};
const response = axios.get('http://localhost:8000/user' , config)
.then(res => res.status === 200 ? true : false)
.catch(err => false);
return response;
}
export default isAuthenticated;
I tried separating them and using async/await :
const isAuthenticated = async () => {
const response = await makeRequest();
return response;
}
const makeRequest = async () => {
const token = localStorage.getItem('jwt');
const config = {headers : {'x-auth-token' : token}};
const response = await axios.get('http://localhost:8000/user' , config)
.then(res => res.status === 200 ? true : false)
.catch(err => false);
return response;
}
And still the same..
After some suggestions :
const isAuthenticated = () => {
const response = makeRequest();
return response;
}
const makeRequest = async () => {
try {
const token = localStorage.getItem('jwt');
const config = {headers : {'x-auth-token' : token}};
const response = await axios.get('http://localhost:8000/user', config);
if (response.status === 200) { // response - object, eg { status: 200, message: 'OK' }
console.log('success stuff');
return true;
}
return false;
} catch (err) {
console.error(err)
return false;
}
}
export default isAuthenticated;
First of all if.
If you are using the default promise then & catch, then the success action should be handled within the 'then' function.
axios.get('http://localhost:8000/user', config)
.then(res => console.log('succesfull stuff to be done here')
.catch(err => console.error(err)); // promise
if you want to use the async/await syntactic sugar, which I personally like it's
const makeRequest = async () => {
try {
const token = localStorage.getItem('jwt');
const config = {headers : {'x-auth-token' : token}};
const response = await axios.get('http://localhost:8000/user', config);
if (response.status === 200) { // response - object, eg { status: 200, message: 'OK' }
console.log('success stuff');
return true;
}
return false;
} catch (err) {
console.error(err)
return false;
}
}
You have to employ the use of async/await,like this:
const isAuthenticated =async () => {
const token = localStorage.getItem('jwt');
if(!token) return false;
const config = {headers : {'x-auth-token' : token}};
const response =await axios.get('http://localhost:8000/user' , config)
.then(res => res.status === 200 ? true : false)
.catch(err => false);
return response;
}
I'm trying to build a react-native app with expo and while trying to sign up I get the following error message stemming from my api.js file:
response.text is not a function. (In 'response.text()', 'response.text' is undefined).
Here is my code:
const BASE_URL = "my local IP:5000";
export const api = async (url, method, body = null, headers = {}) => {
try {
const endPoint = BASE_URL.concat(url);
const reqBody = body ? JSON.stringify(body) : null;
const fetchParams = {method, headers};
if((method === "POST" || method === "PUT") && !reqBody) {
throw new Error("Request body required");
}
if(reqBody) {
fetchParams.headers["Content-type"] = "application/json";
fetchParams.body = reqBody;
}
const fetchPromise = fetch(endPoint, fetchParams);
const timeOutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("Request Timeout");
}, 3000);
});
const response = await Promise.race([fetchPromise, timeOutPromise]);
return response;
} catch (e) {
return e;
}
}
export const fetchApi = async (url, method, body, statusCode, token = null, loader = false)
=> {
try {
const headers = {}
const result = {
token: null,
success: false,
responseBody: null
};
if(token) {
headers["x-auth"] = token;
}
const response = await api(url, method, body, headers);
console.log(response);
if(response.status === statusCode) {
result.success = true;
if(response.headers.get("x-auth")) {
result.token = response.headers.get("x-auth");
}
Here is response.text()
let responseBody;
const responseText = await response.text();
//const responseText = await response.json();
try {
responseBody = JSON.parse(responseText);
} catch (e) {
responseBody = responseText;
}
result.responseBody = responseBody;
return result;
}
Here is response.text()
let errorBody;
const errorText = await response.text();
//const errorText = await response.json();
try {
errorBody = JSON.parse(errorText);
} catch (e) {
errorBody = errorText;
}
result.responseBody = errorBody;
console.log(result);
throw result;
} catch (error) {
return error;
}
}
Any help would be immensely appreciated.
I am trying to show 4 different array's data. I am calling get service but calling it 4 times. instead i want to make one call. with same link but want to dispatch 4 different actions for different data. as you can see there are 4 const which i want to dispatch and i have to make 4 calls right now. i am using initialload() as to reach to my view in Redux.
export function getcoCodeFilter() {
return new Promise((resolve, reject) => {
fetch(getServiceContext() + 'getfilteroptions', {
method: 'GET',
credentials: 'same-origin'
})
.then((response) => {
if (response.ok) {
response
.json()
.then((json) => {
const filterDisplay = json.data;
const companyList = filterDisplay.companyCodes;
const formtypeList = filterDisplay.formTypes;
const yearList = filterDisplay.yearList;
const qtrList = filterDisplay.quarterList;
resolve(companyList);
});
}
else {
response
.json()
.then((json) => {
const errors = json;
reject(errors ? errors.exceptionMessages : []);
});
}
});
});
}
// get filter formtypes
export function getFormTypesFilter() {
return new Promise((resolve, reject) => {
fetch(getServiceContext() + 'getfilteroptions', {
method: 'GET',
credentials: 'same-origin'
})
.then((response) => {
if (response.ok) {
response
.json()
.then((json) => {
const coTypesList = json.data;
resolve(coTypesList.formTypes);
});
}
else {
response
.json()
.then((json) => {
const errors = json;
reject(errors ? errors.exceptionMessages : []);
});
}
});
});
}
// get year for Filters
export function getYearFilter() {
return new Promise((resolve, reject) => {
fetch(getServiceContext() + 'getfilteroptions', {
method: 'GET',
credentials: 'same-origin'
})
.then((response) => {
if (response.ok) {
response
.json()
.then((json) => {
const coTypesList = json.data;
resolve(coTypesList.yearList);
});
}
else {
response
.json()
.then((json) => {
const errors = json;
reject(errors ? errors.exceptionMessages : []);
});
}
});
});
}
// get quarters
export function getQTRFilter() {
return new Promise((resolve, reject) => {
fetch(getServiceContext() + 'getfilteroptions', {
method: 'GET',
credentials: 'same-origin'
})
.then((response) => {
if (response.ok) {
response
.json()
.then((json) => {
const coTypesList = json.data;
resolve(coTypesList.quarterList);
});
}
else {
response
.json()
.then((json) => {
const errors = json;
reject(errors ? errors.exceptionMessages : []);
});
}
});
});
}
export const getInitialLoad = (dispatch) => {
return new Promise((resolve) => {
getcoCodeFilter().then((companyList) => {
dispatch({
type: 'COCODE_FILTER_DISPLAY',
value: companyList
});
resolve();
});
getFormTypesFilter().then((formtypeList) => {
dispatch({
type: 'FORMTYPES_FILTER_DISPLAY',
value: formtypeList
});
resolve();
});
getYearFilter().then((yearList) => {
dispatch({
type: 'YEAR_FILTER_DISPLAY',
value: yearList
});
resolve();
});
getQTRFilter().then((qtrList) => {
dispatch({
type: 'QTR_FILTER_DISPLAY',
value: qtrList
});
resolve();
});
});
};
What I often do is store all information in an object and dispatch an action with the object. The action will get picked up by one more many reducers.
something similar to this.
export const getInitialLoad = (dispatch) => {
const ResponseData = {}
return new Promise((resolve) => {
getcoCodeFilter().then((companyList) => {
ResponseData["companyList"] = companyList;
resolve();
});
getFormTypesFilter().then((formtypeList) => {
ResponseData["formtypeList"] = formtypeList;
resolve();
});
getYearFilter().then((yearList) => {
ResponseData["yearList"] = yearList;
resolve();
});
getQTRFilter().then((qtrList) => {
ResponseData["qtrList"] = qtrList;
dispatch({
type: 'INITIAL_LOAD_ACTION',
value: ResponseData
});
resolve();
});
});
};
INITIAL_LOAD_ACTION can be called anything and used in any number of reducers. all you have to do is set the sate using something along the lines of
action.payload.value.ResponseData where ResponseData is one of the 4 keys you set above.
EDIT:
export const getInitialLoad = async (dispatch) => {
const ResponseData = {}
ResponseData["companyList"] = await getcoCodeFilter();
ResponseData["formtypeList"] = await getFormTypesFilter();
ResponseData["yearList"] = await getYearFilter();
ResponseData["qtrList"] = await getQTRFilter();
dispatch({
type: 'INITIAL_LOAD_ACTION',
value: ResponseData
});
};
OR you could do something like
export const getInitialLoad = async (dispatch) => {
const ResponseData = await Promise.all([getcoCodeFilter, getFormTypesFilter, getYearFilter, getQTRFilter])
dispatch({
type: 'INITIAL_LOAD_ACTION',
value: ResponseData
});
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
I am calling an API which returns results in pages and I am trying to find an 'elegant' way of retrieving them.
Ideally I want to consume them like this:
let results = api.get();
for await (const page of results) {
// do stuff with page
}
I can active close to this using a generator function like this:
class Results {
constructor(url, token) {
this.url = url;
this.token = token;
}
async *page() {
let url = this.url;
while (true) {
const response = await fetch(url, {
headers: { 'Authorization': 'Bearer ' + this.token }
});
const data = await response.json();
yield data.values;
if (!data.next) return;
url = data.next;
}
}
}
And calling it like:
for await (const page of results.page()) {
// do stuff with page
}
I have tried to do it with a [Symbol.iterator] like this, but cannot get it to work:
[Symbol.iterator]() {
let that = this;
return {
next: async function() {
if (!that.page) {
that.page = that.url;
return {done: true};
}
const response = await fetch(that.page, {
headers: { 'Authorization': 'Bearer ' + that.token }
});
const data = await response.json();
that.page = data.data.next;
return {
value: data,
done: false
}
}
}
}
This issue is I need to get the link to the next page from the current page to determine if there is a next page, but as its a promise i cannot access it in the function.
Any ideas how to get the iterator working?
Following advice here is a working function. [Symbol.asyncIterator] made all the difference. Thanks:
[Symbol.asyncIterator]() {
let that = this;
return {
page: that.url,
token: that.token,
next: async function() {
if (!this.page) {
this.page = that.url;
return {done: true};
}
const response = await fetch(this.page, {
headers: { 'Authorization': 'Bearer ' + this.token }
});
const data = await response.json();
this.page = data.next;
return {
value: data,
done: false
}
}
}
Now that its working ideally I just want to be able to iterate through all results and not know about pages so here is a working solution to this for info:
[Symbol.asyncIterator]() {
let that = this;
return {
page: that.url,
token: that.token,
values: [],
next: async function() {
if (!this.page && this.values.length === 0) {
this.page = that.url;
return {done: true};
}
if (this.values.length > 0) {
return {
value: this.values.pop(),
done: false
}
}
const response = await fetch(this.page, {
headers: { 'Authorization': 'Bearer ' + this.token }
});
const data = await response.json();
this.page = data.next;
this.values = data.values;
if (this.values.length === 0) {
return { done: true }
}
return {
value: this.values.pop(),
done: false
}
}
}
This code can be simplified by using an async generator function like so:
async *[Symbol.asyncIterator]() {
let url = this.url;
const getPage = url =>
fetch(url, this.header)
.then(response => response.json())
.then(data => ({
next: data.next,
values: data.values
}));
while(url) {
const page = await getPage(url);
for (const value of page.values) {
yield value;
}
url = page.next;
}
}
So the full class looks like this:
class Response {
constructor(url, token) {
this.url = url;
this.header = {
headers: {
Authorization: `Bearer ${token}`
}
};
}
async* [Symbol.asyncIterator]() {
let url = this.url;
const getPage = url =>
fetch(url, this.header)
.then(response => response.json())
.then(data => ({
next: data.next,
values: data.values
}));
while (url) {
const page = await getPage(url);
for (const value of page.values) {
yield value;
}
url = page.next;
}
}
}
Which allows you to easily loop through paged API results like this:
for await (const item of response) {
...
}