Cannot read property 'then' of undefined for axios wrapper - javascript

I have a bit of a similar issue like this but I can't seem to get it right. I know I have to return a promise and I think I do, although it's still not accepted. Here is my wrapper function for axios calls:
export const callGraph = (url, token) => {
return axios.get(url, {headers: { Authorization: `Bearer ${token}` }})
}
This is the function that invokes callGraph that in turn should return a Promise:
export const getGraphProfile = () => {
if (auth.getAccount()) {
auth.getToken(loginRequest)
.then(response => {
return callGraph(graphConfig.graphMeUrl, response.accessToken)
})
.catch(error => { console.log(error) })
}
}
As you can see I explicitly request return callGraph so I can use it like this:
getGraphProfile()
.then(response => { console.log('givenName ', response.data.givenName) })
.catch(error => console.log(error))
For one reason or another I'm still missing something. Thank you for your help.

You should return the axios promise
export const getGraphProfile = () => {
if (auth.getAccount()) {
return auth.getToken(loginRequest)
.then(response => {
return callGraph(graphConfig.graphMeUrl, response.accessToken)
})
.catch(error => { console.log(error) })
}
}

Related

Uncaught (in promise) Error: Network Error at createError

I'm going to use Axios to communicate API.
But that kind of error keeps coming out. I don't understand this problem. I searched on the Internet and tried everything. Help me.
All I want is to click on that button to see the low value in the developer tool.
useEffect(() => {
setJwt(getClientCookieFromClient('jwt'));
}, []);
const customFetch = async () => {
const res = await axios
.get(`${process.env.NEXT_PUBLIC_WECODE_URI}/subscription/master_table`, {
headers: {
Authentication: jwt,
},
})
.then((res) => res.data);
if (!res.data.success) {
alert(res.data.message);
}
};
...
<button onClick={() => customFetch()}>API호출버튼</button>
Alway wrap await inside try/catch block.
const customFetch = async () => {
try {
const res = await axios
.get(`${process.env.NEXT_PUBLIC_WECODE_URI}/subscription/master_table`, {
headers: {
Authentication: jwt,
},
})
.then((res) => res.data);
if (!res.data.success) {
alert(res.data.message);
}
} catch (error) {
console.log(error);
// Do something with error
}
};
Try
useEffect(() => {
setJwt(getClientCookieFromClient('jwt'));
}, []);
const customFetch = async () => {
const res = await axios.get(`${process.env.NEXT_PUBLIC_WECODE_URI}/subscription/master_table`, {
headers: {
Authentication: jwt,
},
});
if (!res.data.success) {
alert(res.data.message);
}
};
Note:
not sure about you response structure. Current code works as expected for structure:
res = { data: { data: {success: true}}}
if it is not so, then use if statement as !res.success
useEffect(() => {
setJwt(getClientCookieFromClient('jwt'));
}, []);
const customFetch = async () => {
const res = await axios
.get(`${process.env.NEXT_PUBLIC_WECODE_URI}/subscription/master_table`, {
headers: {
Authentication: jwt,
},
})
.then((res) => res.data)
.catch((err) => console.log("Error while fetching",err)); //<--- use .catch for catching error
if (!res.data.success) {
alert(res.data.message);
}
};

Exsport function with Api call then import it on another page and wait for data

I'm new to all this export, import, async so please bare with me.
I have one file where I make API call and export that function so it can be used across other pages.
Of course on other page when function is invoked data payload is not yet there so i get undefind. So i tried to implement async (first time).
Please correct me if this is even possible or I I need some other method.
app.js:
export function inboxMeniIkona () {
//let req = xxxx
fetch(req)
.then(response => {
if (response.ok) {
return response.json()
} else {
throw new Error('NETWORK RESPONSE ERROR')
}
})
.then(data => {
return new Promise(resolve => {
return data // data here is allright
});
})
.catch(error => console.error('FETCH ERROR:', error))
}
And then I tried on other page:
import { inboxMeniIkona } from '~/app'
async function asyncCall() {
console.log('calling');
const result = await inboxMeniIkona();
console.log(result);
// expected output: "resolved"
}
asyncCall();
I'm still getting
CONSOLE LOG: calling
CONSOLE LOG: undefined
Please advise
Add async in your functions and await in your fecth and return it like this.
export async function inboxMeniIkona () {
//let req = xxxx
return await fetch(req)
.then(response => {
if (response.ok) {
return response.json()
} else {
throw new Error('NETWORK RESPONSE ERROR')
}
})
.then(data => {
return data // data here is allright
})
.catch(error => console.error('FETCH ERROR:', error))
}

Save API data into an array with vue.js

I need help doing this. I want to save the user list from the API to an array (called "name"). How can I do this? I tried this forEach but can't make it work. Thanks!
Edit: I wraped the function, still not working.
import axios from 'axios'
export default {
data () {
return {
info: null,
name: []
}
},
mounted () {
axios
.get('http://localhost:3000/api/users/', {mode: 'no-cors'})
.then(response => (this.info = response.data))
.then(() => info.data.forEach(element => {
}))
.catch(error => {
console.log(error)
this.errored = true
})
.finally(this.loading = false)
}
}
From the code above I'm assuming the data in this.info in correct. The problems I see in your code are:
1.
.then(response => (this.info = response.data))
.then(() => info.data.forEach(element => {
}))
info looks to be undefined. I assume this should be this.info.
.then(response => (this.info = response.data))
.then(() => this.info.data.forEach(element => {
}))
Or if you're using the arrow function syntax and returning an assignment expression, you can use
.then(response => (this.info = response.data))
.then(info => info.data.forEach(element => {
}))
Which I don't really recommend, since some linting rules disallow returning assignment expressions (for good reasons). To chain a promise that relies on this implicit language behavior can make code less easily understood.
2.
What forEach does matters. Vue's reactivity does not pick up certain assignment syntax, i.e. this.name[i] = element. You can use array methods like push, but I recommend you use functional programming operators, like map and filter:
.then(() => (this.name = this.info.data.map(element => {
})))
Maybe the this reference is not correct, because on callback methods is on another context, try it:
export default {
data () {
return {
info: null,
name: []
}
},
mounted () {
var self = this
axios
.get('http://localhost:3000/api/users/', {mode: 'no-cors'})
.then(response => (self.info = response.data))
.then(info.data.forEach(element => {
});)
.catch(error => {
console.log(error)
this.errored = true
})
.finally(this.loading = false)
}
}
you forgot to encapsulate the callback into a function receiving the info variable,
try this:
import axios from 'axios'
export default {
data () {
return {
info: null,
name: []
}
},
mounted () {
axios
.get('http://localhost:3000/api/users/', {mode: 'no-cors'})
.then((response) => response.data.forEach(element => {
}))
.catch(error => {
console.log(error)
this.errored = true
})
.finally(this.loading = false)
}
}

How do I mock two fetches or one Promise and one fetch?

I am trying to test the loadAllProjects function.
The test fails at .then() with the error: TypeError: Cannot read property 'then' of undefined
I have also tried mocking the reponse of getHeadersWithToken() but could not get it to work.
Snookered on this one and would appreciate any help.
test:
it('should create SET_ALL_PROJECTS action when fetching projects', () => {
fetch
.once(JSON.stringify([{ access_token: "12345" }]))
.once(JSON.stringify({ name: "x" }))
const expectedActions = [
{ type: "SET_ALL_PROJECTS", json: { name: "x" } },
]
store.dispatch(actions.loadAllProjects.apply())
.then(() => { // FAILS HERE
expect(store.getActions()).toEqual(expectedActions)
})
});
code:
export const getHeadersWithToken = () => {
return fetch("/.auth/me", requestOptions)
.then(parseResponseAndHandleErrors)
.then(json => {
const header = 'Bearer ' + json[0].access_token
const applicationJsonHeaders = getJsonHeaders(header)
return applicationJsonHeaders
})
.catch( error=> {
console.error(error)
})
}
export const loadAllProjects = () => {
return (dispatch) => {
getHeadersWithToken()
.then(applicationJsonHeaders => {
const requestOptions = {
method: 'GET',
headers: applicationJsonHeaders,
};
return fetch(process.env.REACT_APP_PROJECTS_API_URL + "/projects", requestOptions)
.then(parseResponseAndHandleErrors)
.then(json => {
dispatch(setAllProjects(json))})
.catch(error => {
console.error(error)
dispatch(failedToLoadProjects(error))
});
})
}
}
store used in test:
const store = mockStore(Map(
{
allProjects: Map({
}),
currentProject: Map({
authenticationData: Map({
})
})
})
);
What Redux middleware are you using for the async stuff? Make sure you set up the middleware when creating the store for testing.
Since i dont see that anywhere in your code above im gonna assume we are not using that middleware here.
Since loadAllProjects is a higher order function i would do this:
it('should create SET_ALL_PROJECTS action when fetching projects', (done) => {
fetch
.once(JSON.stringify([{ access_token: "12345" }]))
.once(JSON.stringify({ name: "x" }))
const expectedActions = [
{ type: "SET_ALL_PROJECTS", json: { name: "x" } },
]
// Higher order function that returns a new function.
const loadAllProjectsAsync = actions.loadAllProjects();
// The function returned expects a dispatch from Redux as an argument.
// It will do async work and when its done, it will call the provided dispatch.
loadAllProjectsAsync(store.dispatch).then(() => {
expect(store.getActions()).toEqual(expectedActions);
done();
})
});
You also need to modify your code for loadAllProjects so that the inner function returns the promise:
export const loadAllProjects = () => {
return (dispatch) => {
// You will need to return the promise in order for the test to be able to call .then() on it.
return getHeadersWithToken()
.then(applicationJsonHeaders => {
const requestOptions = {
method: 'GET',
headers: applicationJsonHeaders,
};
return fetch(process.env.REACT_APP_PROJECTS_API_URL + "/projects", requestOptions)
.then(parseResponseAndHandleErrors)
.then(json => {
dispatch(setAllProjects(json))})
.catch(error => {
console.error(error)
dispatch(failedToLoadProjects(error))
});
})
}}
Also, as already stated, you must tell the jest when the test is done if you are testing async stuff. Do this by letting your it call take done as a param and call that as a function after you have verified the outcome i the .then()
This was just a quick fix from my side. There still might be something im missing or some bugs in the code above, but you get the point.
Let me know if you have any follow up questions?
In case this is ever useful to anyone and in acknowledgment of Septastium's answer, I eventually changed the code to:
async getHeadersWithToken(requestType) {
if (process.env.REACT_APP_RUNNING_LOCALLY==="true") {
return {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
};
}
let result = await fetch("/.auth/me", this.requestOptions)
let headers = result.json()
.then( json => {
const header = 'Bearer ' + json[0].access_token
const applicationJsonHeaders = this.getJsonHeaders(header, requestType)
return applicationJsonHeaders
})
.catch(error => {
console.error(error)
})
return headers
}
export const loadAllProjects = () => {
return async dispatch => {
const authenticator = new Authenticator()
let applicationJsonHeaders = await authenticator.getHeadersWithToken(constants.GET)
let loggedInUser = await authenticator.getLoggedInUser()
const requestOptions = {
method: 'GET',
headers: applicationJsonHeaders,
};
return await fetch(process.env.REACT_APP_PROJECTS_API_URL + "/projects", requestOptions)
.then(response => {
return parseResponseAndHandleErrors(response)
})
.then(json => dispatch(setAllProjects(json)))
.then(()=> dispatch(setAuthenticationData(loggedInUser)))
.catch(error => {
console.error(error)
return dispatch(failedToLoadProjects(error))
});
}
}
and the test to:
const checkActionsWereDispatched = async (expectedActions, actionCreator) => {
const store = mockStore(Map(
{
}),
);
store.dispatch(await actionCreator.apply()).then(() => {
expect(store.getActions()).toEqual(expectedActions)
})
}
it('should create SET_ALL_PROJECTS action when fetching projects', async () => {
fetch
.once(JSON.stringify([{ access_token: "12345" }]))
.once(JSON.stringify({ name: "x" }))
const expectedActions = [
{ type: "SET_ALL_PROJECTS", json: { name: "x" } },
]
checkActionsWereDispatched(expectedActions, actions.loadAllProjects)
});
As noted above I think Spetastium's version of the test is easier to read than mine and his article here was very helpful.

Merge api request using promise

Due to the api of a plugin I'm using not working properly. I need to merge the two different requests. I am using the thunk below.
I can get a response but I cannot seem to check for response.ok, and return the combined data:
export function fetchCategories() {
const firstPage =
"http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page=1";
const secondPage =
"http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page=2";
return dispatch => {
dispatch(isLoading(true));
Promise.all([fetch(firstPage), fetch(secondPage)])
.then(response => {
// check for ok here
response.ForEach(response => {
if (!response.ok) throw Error(response.statusText);
});
dispatch(isLoading(false));
return response;
})
.then(response => response.json())
// dispatch combined data here
.then(data => dispatch(fetchSuccessCategories(data)))
.catch(() => dispatch(hasErrored(true)));
};
}
Any ideas?
You are doing the check for .ok fine because it's in a loop, but your response is actually an array of two Response objects, it does not have a .json() method. You could do Promise.all(responses.map(r => r.json())), but I would recommend to write a helper function that does the complete promise chaining for one request and then call that twice:
function fetchPage(num) {
const url = "http://wordpress.rguc.co.uk/index.php/wp-json/tribe/events/v1/categories?per_page=60&page="+num;
return fetch(url).then(response => {
if (!response.ok)
throw new Error(response.statusText);
return response.json();
});
}
export function fetchCategories() {
return dispatch => {
dispatch(isLoading(true));
Promise.all([fetchPage(1), fetchPage(2)]).then(data => {
dispatch(isLoading(false));
dispatch(fetchSuccessCategories(merge(data)));
}, err => {
dispatch(isLoading(false));
dispatch(hasErrored(true));
});
};
}

Categories