unable to display data dynamically using fetch API - javascript

I am facing problems while displaying data fetched dynamically from an API. console log clearly shows that the promise has been fulfilled and the data has been successfully fetched but the data is somehow not getting displayed in the relevant container. I am using map method of arrays to dynamically display data using template strings. An inspection of the page also does not mentions any related errors in the code. what could be the problem?
const url = 'https://api.github.com/users/john-smilga/followers?per_page=100'
const fetchFollowers = async() => {
const response = await fetch(url);
const data = await response.json();
return data;
}
const getData = fetchFollowers();
const place = document.getElementById("container")
const display = () => {
let newFollowers = getData.map((item) => {
const {
avatar_url,
login,
html_url
} = item;
return `<article class="card">
<img src="${avatar_url}">
<h4> ${login}</h4>
view profile
</article>`;
})
.join(" ");
place.innerHTML = newFollowers;
}
<div id="container"></div>

You haven't called display function anywhere. Along with it, you also should call fetchFollowers with a proper async/await to wait for a response, and then you can use it for data population on HTML.
const url = 'https://api.github.com/users/john-smilga/followers?per_page=100'
const fetchFollowers = async () => {
const response = await fetch(url);
const data = await response.json();
return data;
}
const display = async () => {
//fetch data
const getData = await fetchFollowers();
const place = document.getElementById("container");
let newFollowers = getData.map((item) => {
const {
avatar_url,
login,
html_url
} = item;
return `<article class="card">
<img src="${avatar_url}">
<h4> ${login}</h4>
view profile
</article>`;
})
.join(" ");
place.innerHTML = newFollowers;
}
//call `display` function
display();
<div id="container"></div>

Hi Salman when you are calling fetch followers the function is returning promise and you are trying to iterate over a promise object instead you need to wait for the data to be fetched before iterating over it there are multiple ways to do this adding one of the methods to fix your code.
const url = 'https://api.github.com/users/john-smilga/followers?per_page=100'
const fetchFollowers = async () => {
const response = await fetch(url);
const data = await response.json();
return data;
}
const display = async () => {
const getData = await fetchFollowers();
console.log(getData);
const place = document.getElementById("container")
let newFollowers = getData.map((item)=> {
const { avatar_url, login, html_url } = item;
return `<article class="card">
<img src="${avatar_url}">
<h4> ${login}</h4>
view profile
</article>` ;
})
.join(" ");
place.innerHTML =newFollowers;
}
display();
You can update the above code as per your needs. Hope you understand the underlying problem.

that line:
const getData =fetchFollowers();
return a promise, but not a result from a promise
wrap all code in async function and:
const getData = await fetchFollowers();
use kind of promise syntax:
fetchFollowers().then(getData=>{console.log(getData); ...other code...});

Related

How to escape this callback hell

I'm currently trying to fetch data from public API about a country and its neighboring countries to render on my html.
renderCountry( ) is a function to implement on my html with the data I will receive.
I also excluded some unnecessary codes, which I believe is not major in this particular case.
This is how I fetch data:
const getCountryAndNeighbour = function(country) {
fetch(`https://restcountries.com/v2/name/${country}`)
.then(response => response.json())
.then(data => {
renderCountry(data[0]);
const neighbour = data[0].borders;
neighbour.forEach(country => {
fetch(`https://restcountries.com/v2/alpha/${country}`)
.then(response => response.json())
.then(data => renderCountry(data, `neighbour`))
});
})
}
Here, you will see callback hell architecture. Any idea for escape from that?
Thanks in advance.
You can try using async/await. You would add async before the function keyword and add await as needed. See below to see this in action:
const getCountryAndNeighbour = async function (country) {
const res = await fetch(`https://restcountries.com/v2/name/${country}`)
const data = await res.json();
renderCountry(data[0]);
const neighbour = data[0].borders;
await Promise.all(
neighbour.map(async country => {
let response = await fetch(`https://restcountries.com/v2/alpha/${country}`)
response = await response.json();
return renderCountry(response, 'neighbour');
});
);
}
You can rewrite it using async/await
eg.
const getCountryAndNeighbour = async country => {
const response = await fetch(`https://restcountries.com/v2/name/${country}`);
const data = await response.json();
renderCountry(data[0]);
const neighbour = data[0].borders;
neighbour.forEach(async country => {
const response = await fetch(`https://restcountries.com/v2/alpha/${country}`)
const data = await response.json();
renderCountry(data, `neighbour`);
});
};
Please note that forEach will run all promises in the same time.
If you want to run one by one you should use eg. for loop or some util like Bluebird.map which allows you to specify a concurrency
Good luck!
This will do using Async/await
async function getCountryData(country) {
const response = await fetch(`https://restcountries.com/v2/name/${country}`);
return await response.json();
}
async function getNeighbourData(country) {
const response = await fetch(`https://restcountries.com/v2/alpha/${country}`);
return await response.json();
}
async function getCountryAndNeighbour(country) {
const data = await getCountryData(country);
const neighbourCountries = data[1].borders;
for (const neighbour of neighbourCountries) {
const response = await getNeighbourData(neighbour);
console.log(response);
}
}
Add the necessary validations when checking [0]/[1] in your function.

React jsonserver promise result issue

I am creating a react/ redux app with json fake api server I am trying to add a login and trying to get data from json fake api server, data is showing and all ok , but data is always resulting as a promise and the required data is inside the promise. i tried many ways to distructure but throwing errors , could anyone help me on this,
my axios request
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};
const newout2 = userslist();
const newout = newout2;
console.log(newout);
the place where I am using it
export const login = (credentials) => (dispatch) => {
return new Promise((resolve, reject) => {
const matchingUser =
newout2 &&
newout2.find(({ username }) => username === credentials.username);
if (matchingUser) {
if (matchingUser.password === credentials.password) {
dispatch(setUser(matchingUser));
resolve(matchingUser);
} else {
dispatch(setUser(null));
reject("Password wrong");
}
} else {
dispatch(setUser(null));
reject("No user matching");
}
});
};
i am getting this error
You are using then in your userslist method while awaiting in an async method. drop the then and just use proper await inside an async method.
const urlss = "http://localhost:5000/users";
export const userslist = async () => {
const r = await axios.get(urlss);
const data = r.data;
return data;
};

How to store data from a async function in a new array?

I'm pretty new to this and I'm stuck. I try to understand how I can store data from a json file in a new variable. I can log this out to the console, but I want to get this array in a new variable. Every video or tutorial I watched just did that... logs it out to the console.
Here is my code
const getData = async () => {
const response = await fetch ('data.json');
const data = await response.json();
return data;
};
getData()
.then((data)=> console.log(data));
Thanks for your help!
Here you go, but as #Guerric P mentioned in the comments, you probably must ask yourself why you need to do this
const getData = async () => {
const response = await fetch ('data.json');
const data = await response.json();
return data;
};
const putDataToVariable = async () => {
const variable = await getData()
console.log(variable)
}
putDataToVariable().then()

How to fetch data from multiple urls at once?

I have a function that fetches from a url in React
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
useEffect(() => {
const fetchCocktailList = async () => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
setLoading(true);
try {
const res = await fetch(`${baseUrl}search.php?s=margarita`);
const data = await res.json();
console.log(data);
setCocktails(data.drinks);
setLoading(false);
} catch (err) {
console.log('Error fetching data');
setLoading(false);
}
};
fetchCocktailList();
}, []);
How I'm mapping data so far.
const DrinkList = () => {
const { cocktails } = useContext(DataContext);
return (
<div className='drink-list-wrapper'>
{cocktails.length > 0 &&
cocktails.map((drink) => {
return <DrinkItem drink={drink} key={drink.idDrink} />;
})}
</div>
);
};
However I want to fetch from this url also ${baseUrl}search.php?s=martini
I would like a good clean way to do this and set my state to both of the returned data.
First base the data fetch function on a parameter:
const fetchCocktail = async (name) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/';
try {
const res = await fetch(`${baseUrl}search.php?s=` + name);
const data = await res.json();
return data.drinks;
} catch (err) {
console.log('Error fetching data');
}
}
Then use Promise.all to await all results:
setLoading(true);
var promises = [
fetchCocktail(`margarita`),
fetchCocktail(`martini`)
];
var results = await Promise.all(promises);
setLoading(false);
DrinkList(results);
Where results will be an array with the responses that you can use on the DrinkList function.
Here's a method which will let you specify the cocktail names as dependencies to the useEffect so you can store them in your state and fetch new drink lists if you want new recipes. If not, it'll just be a static state variable.
I've also added another state variable errorMessage which you use to pass an error message in the case of failure.
Also, you should include the appropriate dependencies in your useEffect hook. The setState functions returned by calls to useState are stable and won't trigger a re-run of the effect, and the cocktailNames variable won't trigger a re-run unless you update it with new things to fetch.
const DataContextProvider = (props) => {
const [isLoading, setLoading] = useState(false);
const [cocktails, setCocktails] = useState([]);
const [errorMessage, setErrorMessage] = useState(''); // holds an error message in case the network request dosn't succeed
const [cocktailNames, setCocktailNames] = useState(['margarita', 'martini']); // the search queries for the `s` parameter at your API endpoint
useEffect(() => {
const fetchCocktailLists = async (...cocktailNames) => {
const fetchCocktailList = async (cocktailName) => {
const baseUrl = 'https://www.thecocktaildb.com/api/json/v1/1/search.php';
const url = new URL(baseUrl);
const params = new URLSearchParams({s: cocktailName});
url.search = params.toString(); // -> '?s=cocktailName'
const res = await fetch(url.href); // -> 'https://www.thecocktaildb.com/api/json/v1/1/search.php?s=cocktailName'
const data = await res.json();
const {drinks: drinkList} = data; // destructured form of: const drinkList = data.drinks;
return drinkList;
};
setLoading(true);
try {
const promises = [];
for (const cocktailName of cocktailNames) {
promises.push(fetchCocktailList(cocktailName));
}
const drinkLists = await Promise.all(promises); // -> [[drink1, drink2], [drink3, drink4]]
const allDrinks = drinkLists.flat(1); // -> [drink1, drink2, drink3, drink4]
setCocktails(allDrinks);
}
catch (err) {
setErrorMessage(err.message /* or whatever custom message you want */);
}
setLoading(false);
};
fetchCocktailList(...cocktailNames);
}, [cocktailNames, setCocktails, setErrorMessage, setLoading]);
};
var promises = [
fetchCocktail(api1),
fetchCocktail(api2)
];
var results = await Promise.allSettled(promises);

Returning promises in javascript

I need some help returning a promise. I don't get why I am not getting the players value
The code
const playerSelectName = document.getElementById('sel1');
const playerSelectPosition = document.getElementById('sel2');
const playerSelectAge = document.getElementById('sel3');
const searchButton = document.getElementById('search-btn');
async function getPlayers() {
const response = await fetch('https://football-players-b31f2.firebaseio.com/players.json?print=pretty');
const playersObject = await response.json();
return playersObject;
}
searchButton.addEventListener('click', async () => {
const players = await getPlayers();
let [ name, position, age ] = [ playerSelectName.value, playerSelectPosition.value, playerSelectAge.value ];
yersObject.filter((playerAge) => {});
});
I cant get to access the code inside my listener function of the value players
the problem was in destructuring check the below snippet. I hope this will solve the issue . Also please update what you are trying to achieve in filter so i can add on the solution.
I didnt understood what are you trying to do in destructure part and after that filter
async function getPlayers() {
const response = await fetch('https://football-players-b31f2.firebaseio.com/players.json?print=pretty');
const playersObject = await response.json();
return playersObject;
}
const searchButton = document.getElementById('search-btn');
searchButton.addEventListener('click', async () => {
const players = await getPlayers();
players.forEach(player => {
const {name, nationality, position} = player
console.log(name, nationality, position)
})
});
<button id='search-btn'>
Load Results
</button>
Here's a simple example accessing the players to help get you started.
async function getPlayers() {
const response = await fetch('https://football-players-b31f2.firebaseio.com/players.json?print=pretty');
return response.json();
}
(async () => {
const players = await getPlayers();
console.log(players)
})();

Categories