How to get rid of promise when getting json data - javascript

I'm trying to simply get data from a weather API. Here's my api link to get http://api.wunderground.com/api/5b81d144ae2d1942/conditions/q/46.838260,-71.293689.json
In my api.js file, i have this basic function :
const baseUrl = `http://api.wunderground.com/api/5b81d144ae2d1942/conditions/q`;
export const getCurrent = (lat,lon) => {
return fetch(`${baseUrl}/${lon},${lat}.json`)
.then((response) => response.json())
.then((json) => {
console.log(json.current_observation.weather)
return json.current_observation
})
.catch(() => {
console.error('unable to fetch tasks')
})
}
Notice the console.log, in this function i'm able to fetch the json data, i got the value i want.
Now, in my Vue, i call this function this way :
export default {
data: () => ({
current: []
}),
created: function () {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(this.showPosition);
}
},
methods: {
showPosition(position) {
const data = api.getCurrent(position.coords.longitude,position.coords.latitude);
this.current = data;
console.log(this.current);
}
}
}
For some reason, the console.log in here gives me this :
PromiseĀ {<pending>}__proto__:
Promise[[PromiseStatus]]: "resolved"
[[PromiseValue]]: Object
I don't know what's going on but i can't access the data. I searched on the net, a lot of pages talk about this, but couldn't find the exact solution, only long texts ...
Is there a solution for this (code please)
Thanks a lot.

To "get rid" of the Promise and access it's data, use .then() in .getCurrent()'s result, just like you are when using fetch():
methods: {
showPosition(position) {
api.getCurrent(position.coords.longitude,position.coords.latitude)
.then((data) => {
this.current = data;
console.log(this.current);
}
}
}
Alternatively, you could declare showPosition as async, and use await:
methods: {
showPosition: async function(position) {
const data = await api.getCurrent(position.coords.longitude,position.coords.latitude);
this.current = data;
console.log(this.current);
}
}
Just keep in mind the results of both executions will be processed asynchronously, meaning this.current will not have the value of data right away.

Related

Is it possible to call to APIs inside a react-router loader function

I'd like to know if it's possible to make 2 API calls inside a loader function if I am using react-router 6. My ideas was to create an object based on these 2 calls and destruct the object in the rendering component like this:
function MainComponent (){
const {data , reservation} = useRouteLoaderData('room-details');
..
..
}
export default MainComponent;
export async function loader({request, params}) {
const id = params.roomId;
const response = await fetch ('http://localhost:8080/rooms/' + id);
const response2 = await fetch('http://localhost:8080/rooms/reservation/' + id)
const megaResponse = {
data: response, //i tried data:{respose} It ain't work
reservation: response2,
};
if (!response.ok) {
throw json({message: 'Something Wrong'}, {status: 500});
}
else {
return megaResponse;
}
}
But i have no success output.
I'd really want to make these 2 call in one place, otherwise I will have to use useEffect in a child component. Not a good Idea I think.
Thanks
I suspect you are not returning the unpacked response, i.e. JSON. I suggest surrounding the asynchronous code in a try/catch and simply try to process the requests/responses. Unpack the JSON value from the response objects. Since it doesn't appear the requests are dependent on one another I recommend loading them into an array of Promises that can be run concurrently and awaited as a whole. If during any part of the processing a Promise is rejected or an exception thrown, the catch block will return the JSON error response to the UI, otherwise, the { data, reservation } object is returned.
const loader = async ({ request, params }) => {
const { roomId } = params;
try {
const [data, reservation] = await Promise.all([
fetch("http://localhost:8080/rooms/" + roomId),
fetch("http://localhost:8080/rooms/reservaton/" + roomId)
]).then((responses) => responses.map((response) => response.json()));
return { data, reservation };
} catch {
throw json({ message: "Something Wrong" }, { status: 500 });
}
};
I found the solution, I tried it and it worked. It is as follow:
function MainComponent (){
const [data , reservation] = useRouteLoaderData('room-details');
..
..
}
export default MainComponent;
export async function loader({request, params}) {
const id = params.roomId;
return Promise.all([
fetch ('http://localhost:8080/rooms/' + id),
fetch('http://localhost:8080/rooms/reservation/' + id)
])
.then(
([data, reservation]) =>
Promise.all([data.json(), reservation.json()]),
error => {throw json({message: 'Something Wrong'}, {status: 500});}
)
.then(([data, reservation]) => {
return [data, reservation];
});
}
Thanks

Error when using a function that requests API data

I'm working on this app that takes data from a movies API and I want to work with it.
I have this function that gets the API data:
/** #format */
const fetchMovie = movie => {
var APIKEY = "xxxxxxxxxxxxxxxxxxxxxx";
var API2 =
"https://api.themoviedb.org/3/search/movie?api_key=xxxxxxxxxx&language=en-US&page=1&include_adult=false&query=avengers";
var API = `https://api.themoviedb.org/3/search/movie?api_key=${APIKEY}&language=en-US&page=1&query=${movie}`;
fetch(API2)
.then(data => data.json())
.then(movies => console.log(movies) || movies.items)
.catch(error => {
console.log(error);
return null;
});
};
export default fetchMovie;
And I have this App class that uses the API data:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
activeMovie: "Avengers",
loading: true,
allMovies: []
};
}
componentDidMount() {
this.getData(this.activeMovie);
}
componentDidUpdate(prevState) {
if (prevState.activeMovie !== this.state.activeMovie) {
this.getData(this.state.activeMovie);
}
}
getData(movie) {
this.setState({
loading: true
});
fetchMovie(movie).then(data => {
this.setState({
allMovies: data,
loading: false
});
});
}
Now, before this I have used the same methodology and it worked but I don't know why the I get
TypeError: Object(...)(...) is undefined // this line fetchMovie(movie).then(data => {
The API is good, I can console log the data before it gets to the App component, but the function in the app component somehow doesn't work. any clues?
That's simply because your function fetchMovie() doesn't return a Promise so that you than use .then() after it. You can return a promise instead. However the logic in your code is probably a bit shaky. You might as well look that up because it goes into an infinite loop, consider debugging component life cycles for that.
To return a promise from your function, you can use a similar approach as I wrote in here: https://codesandbox.io/s/small-sun-sfcyv.
You are not returning any promise from your fetchMovie function, that way you can't use the .then so right now you only have access to that data in your fetchMovie. A possible solution would be defining your function as async and then you would be able to return your data from that function.
Try this.
/** #format */
const fetchMovie = movie => {
var APIKEY = "xxxxxxxxxxxxxxxxxxxxxx";
var API2 =
"https://api.themoviedb.org/3/search/movie?api_key=xxxxxxxxxx&language=en-US&page=1&include_adult=false&query=avengers";
var API = `https://api.themoviedb.org/3/search/movie?api_key=${APIKEY}&language=en-US&page=1&query=${movie}`;
return fetch(API2)
.then(data => data.json())
.then(movies => console.log(movies) || movies.items)
.catch(error => {
console.log(error);
return null;
});
};
export default fetchMovie;

Data coming in Service Response but showing 'undefined' from where it is called - JavaScript | React

I am calling an API which in turns gives me data back. When I am trying to store that data in variable and then console.log, the result is undefined. Why is it happening? How to fix this?
someService.js
getItems: () => {
axios.get('https://jsonplaceholder.typicode.com/users/')
.then( response => {
console.log(response.data); //gives me an array of 10 items
return response.data;
})
}
someReducer.js
case "GET_ITEM_LIST": {
let data = getItemsAPI.getItems();
console.log(data); //gives undefined
return {
...state,
items: data
}
}
With your case, you fetching API from server as asynchronous:
Try to you async/await like this:
export default {
fetch: async (state, { type, payload }) => {
// ....
case "GET_ITEM_LIST": {
let data = await getItemsAPI.getItems();
console.log(data); // you will see data here
return {
...state,
items: data
}
}
// ....
}
}
You need to return promise and await for result as its async. Notice return before axios that returns promise that you can work on. (I'm editing in mobile and I guess formatting is bad. If it worked, will update later)
getItems: () => {
return axios.get('https://jsonplaceholder.typicode.com/users/')
.then( response => {
console.log(response.data); //gives me an array of 10 items
return response.data;
})
}
Then, await getItems() or getItems().then() wherever you need.

Using React.setState in componentDidMount() for data returned within nested promises?

I'm trying to put some data into state in a React app. The flow involves fetching a list of IDs from the HackerNews API, then taking each ID and making an additional API call to fetch the item associated with each ID. I ultimately want to have an array of 50 items in my component state (the resulting value of the each '2nd-level' fetch.
When I setState from JUST the single 'top-level' promise/API call, it works fine and my state is set with an array of IDs. When I include a second .then() API call and try to map over a series of subsequent API calls, my state gets set with unresolved Promises, then the fetch() calls are made.
I'm sure this a problem with my poor grasp on building appropriate async methods.
Can someone help me figure out what I'm doing wrong, and what the best practice for this is??
My component:
import React from 'react'
import { fetchStoryList } from '../utils/api'
export default class Stories extends React.Component {
state = {
storyType: 'top',
storyList: null,
error: null,
}
componentDidMount () {
let { storyType } = this.state
fetchStoryList(storyType)
.then((data) => {
console.log("data", data)
this.setState({ storyList: data })
})
.catch((error) => {
console.warn('Error fetching stories: ', error)
this.setState({
error: `There was an error fetching the stories.`
})
})
}
render() {
return (
<pre>{JSON.stringify(this.state.storyList)}</pre>
)
}
}
My API Interface:
// HackerNews API Interface
function fetchStoryIds (type = 'top') {
const endpoint = `https://hacker-news.firebaseio.com/v0/${type}stories.json`
return fetch(endpoint)
.then((res) => res.json())
.then((storyIds) => {
if(storyIds === null) {
throw new Error(`Cannot fetch ${type} story IDs`)
}
return storyIds
})
}
function fetchItemById(id) {
const endpoint = `https://hacker-news.firebaseio.com/v0/item/${id}.json`
return fetch(endpoint)
.then((res) => res.json())
.then((item) => item)
}
export function fetchStoryList (type) {
return fetchStoryIds(type)
.then((idList) => idList.slice(0,50))
.then((idList) => {
return idList.map((id) => {
return fetchItemById(id)
})
})
//ABOVE CODE WORKS WHEN I COMMENT OUT THE SECOND THEN STATEMENT
You are not waiting for some asynchronous code to "finish"
i.e.
.then((idList) => {
return idList.map((id) => {
return fetchItemById(id)
})
})
returns returns an array of promises that you are not waiting for
To fix, use Promise.all
(also cleaned up code removing redundancies)
function fetchStoryIds (type = 'top') {
const endpoint = `https://hacker-news.firebaseio.com/v0/${type}stories.json`;
return fetch(endpoint)
.then((res) => res.json())
.then((storyIds) => {
if(storyIds === null) {
throw new Error(`Cannot fetch ${type} story IDs`);
}
return storyIds;
});
}
function fetchItemById(id) {
const endpoint = `https://hacker-news.firebaseio.com/v0/item/${id}.json`
return fetch(endpoint)
.then(res => res.json());
}
export function fetchStoryList (type) {
return fetchStoryIds(type)
.then(idList => Promise.all(idList.slice(0,50).map(id => fetchItemById(id)));
}
One solution would be to update fetchStoryList() so that the final .then() returns a promise that is resolved after all promises in the mapped array (ie from idList.map(..)) are resolved.
This can be achieved with Promise.all(). Promise.all() take an array as an input, and will complete after all promises in the supplied array have successfully completed:
export function fetchStoryList(type) {
return fetchStoryIds(type)
.then((idList) => idList.slice(0,50))
.then((idList) => {
/* Pass array of promises from map to Promise.all() */
return Promise.all(idList.map((id) => {
return fetchItemById(id)
});
});
}

How to mock function, that is called in "then" in axios promise?

I have a function, that is fetching data from the backend. When fetch is successful, it extracts one value from the response, and then call another function (parseAllRecordsData), that is converting the value into other value. I'm trying to test this function, but after mocking parseAllRecordsData function, it's still trying to call original function (and throws errors from that function).
In other tests jest.fn or jest.spy is working correctly, but when I'm trying to mock function that is used in "then" it's not.
export function fetchAllRecordsData(payload) {
const url = `/apis/${payload.link.split('apis/')[1]}`;
return axios.get(url)
.then(({ data }) => {
if (data && data._embedded) {
const parsedData = data._embedded['taxonomies:entry'];
const arrayData = parseAllRecordsData(parsedData, payload);
return { data: List(arrayData) };
}
return { data: List([]) };
})
.catch((error) => ({ error }));
}
And my test:
describe('fetchAllRecordsData', () => {
const mockedPayload = {
link: 'apis/ok_link',
};
beforeAll(() => {
jest.spyOn(LegalListRecordsApi,'parseAllRecordsData').mockReturnValue(['test']);
});
it('test', async () => {
const test = await LegalListRecordsApi.fetchAllRecordsData(mockedPayload);
expect(test).toEqual(1);
});
});
When it's called like this, parseAllRecordsData calls real function, and throws the error, because mocked Axios response doesn't have some values that parsing function use. I'm only interested in return value, not calling this function.
jest.spyOn(LegalListRecordsApi,'parseAllRecordsData').mockReturnValue(['test']); mocks the module export for parseAllRecordsData.
This doesn't have any effect on fetchAllRecordsData because it is in the same module as parseAllRecordsData and is calling it directly.
ES6 modules supports cyclic dependencies so you can import a module into itself.
Import the module into itself and use the module to call parseAllRecordsData:
import * as LegalListRecordsApi from './LegalListRecordsApi'; // import module into itself
export function fetchAllRecordsData(payload) {
const url = `/apis/${payload.link.split('apis/')[1]}`;
return axios.get(url)
.then(({ data }) => {
if (data && data._embedded) {
const parsedData = data._embedded['taxonomies:entry'];
const arrayData = LegalListRecordsApi.parseAllRecordsData(parsedData, payload); // use the module
return { data: List(arrayData) };
}
return { data: List([]) };
})
.catch((error) => ({ error }));
}
...and the call will be mocked when you mock the module export for parseAllRecordsData.
export function fetchAllRecordsData(payload, axiosInterface = axios) {
return return axiosInterface.get(url)
. then(({ data }) => {
// your code
})
.catch((error) => ({ error }))
}
So, you need create mock object with method get, method get should return promise.

Categories

Resources