Im using an API and I fetch 9 pictures every time from said API. the API can display "pages", meaning if a "page=1" parameter is passed in the request I get the first 9 pictures from the database. "page=2" gives me the next 9 pictures and so on.
I want to have 2 buttons so on every click I get the next 9 pictures. I tried to do so using the API parameter change but haven't had much luck.
I ran across this thread:
Setting query string using Fetch GET request
but either it doesnt help me or I dont understand it enough.
Thanks!
my code is below:
const Home = ()=>{
let pageNumber = 1;
const [photosData, SetPhotosData] = useState([])
const url = `https://pixabay.com/api/?key=25540812-faf2b76d586c1787d2dd02736&per_page=9&page=`
const getImages = async()=>{
try {
const response = await fetch (url+pageNumber)
if (!response.ok) {
throw new Error();
}
const responseObj = await response.json();
const allPhotosData = responseObj.hits
SetPhotosData(allPhotosData)
}
catch (error) {
console.log(error);
}
}
useEffect(()=>{
getImages();
},[])
const getNextPage = ()=> {
pageNumber++;
console.log(pageNumber);
getImages();
}
const getPrevPage =()=>{
pageNumber--;
console.log(pageNumber);
getImages();
}
return(
<div>
<button onClick={getPrevPage}>prev</button>
<button onClick={getNextPage}>next</button>
<br/>
{photosData.map(singlePhotoData=>
<img
key={singlePhotoData.id}
src={singlePhotoData.largeImageURL}
/>)}
</div>
)
}
export default Home;
Your pageNumber will reset to 1 everytime you set a new state with SetPhotosData. You could fix this by using useRef.
const pageNumber = useRef(1);
...
const response = await fetch(url + pageNumber.current);
...
const getNextPage = () => {
pageNumber.current++;
console.log(pageNumber.current);
getImages();
};
const getPrevPage = () => {
if (pageNumber.current < 2) return;
pageNumber.current--;
console.log(pageNumber.current);
getImages();
};
Note that we also check if the pageNumber is not lower than 2 before decreasing it and calling the api to prevent the api from throwing a error, saying it has no page 0
Related
I am trying to make barbershop web app where costumer can see list of free appointments and when they reserve free appointment I want to delete that field from firebase.
I have a collection which represents one barber.
This is how it looks in firebase.
As you see radno_vrijeme is object or map in firebase which contains 6 arrays, and in each array there is list of free working hours.
In my function I am able to do everthing except last line where I need to update firebase collection.
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDoc(q);
querySnap.forEach(async (doc) => {
const radnoVrijeme = doc.data().radno_vrijeme;
// Find the index of the hour you want to delete
const index = radnoVrijeme["Mon"].indexOf(hour);
// Remove the hour from the array
radnoVrijeme["Mon"].splice(index, 1);
// Update the document in the collection
console.log(radnoVrijeme);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
await freeTimeRef.update({ radno_vrijeme: radnoVrijemeMap });
});
} catch (error) {
console.log(error);
}
};
I tried to pass it as JSON stringified object, but it didn't work. I always get this error :
"FirebaseError: Expected type 'ya', but it was: a custom Ia object"
When you are trying to fetch multiple documents using a collection reference or query, then you must use getDocs():
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDocs(q);
const updates = [];
querySnap.forEach((d) => {
const radnoVrijeme = d.data().radno_vrijeme;
const index = radnoVrijeme["Mon"].indexOf(hour);
radnoVrijeme["Mon"].splice(index, 1);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
updates.push(updateDoc(d.ref, { radno_vrijeme: radnoVrijemeMap }))
});
await Promise.all(updates);
console.log("Documents updated")
} catch (error) {
console.log(error);
}
};
getDoc() is used to fetch a single document using a document reference.
I am new to JavaScript and this is my first question here. I've been trying for week to render my RapidApi data on another HTML page. I made search form on my index page and then put its values as my api call parameters in order to influence my API response. I used fetch to do so. The issue is that my API data keeps rendering on the same index page which is understandable since I don't know how to render it on a separate page. This also means that my CSS styling options are limited since I cannot design API data as I want without messing up my index page. If you have any sort of solution that is not way too complicated I would really appreciate your help.
Here is part of my code:
const input = document.getElementById(`location`);
const guests = document.getElementById(`guests`);
const check = document.querySelectorAll(".date");
let id;
document.getElementById(`submit`).addEventListener(`click`, function (e) {
e.preventDefault();
locationId();
});
async function locationId () {
let hotelId = input.value;
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': '//API key goes here',
'X-RapidAPI-Host': 'tripadvisor16.p.rapidapi.com'
}
};
let response = await fetch(`https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchLocation?query=${hotelId}`, options);
if (!response.ok) throw new Error(`Woops something went wrong`);
let data = await response.json();
let geoId = await (data.data[0].geoId);
id= parseInt(geoId);
return (fetch(`https://tripadvisor16.p.rapidapi.com/api/v1/hotels/searchHotels?geoId=${id}&checkIn=${check[0].value}&checkOut=${check[1].value}&pageNumber=1&adults=${guests.value}currencyCode=USD`, options))
.then(response => response.json())
.then(data => {
let list = data.data.data;
displayObjectElements(list)
function displayObjectElements (object) {
let display = ``;
let price = ``;
object.forEach(element => {
display+= `<div class = "objectResults">
<ul class="hotel__lists">
<li><h2 class = "title">${element.title}</h2></li>
<li><img class= "hotels--photo "src="${element.cardPhotos[0].sizes.urlTemplate.split("?")[0] + `?w=500&h=500`}" alt=image--photo/></li>
<li><p>Ranking:${element.bubbleRating.rating}☆ out of 5☆</p></li>`
if(!element.priceForDisplay) {
display+= `<li><p>There is no price to display</p></li>`
display+= `<li><button class="booking-btn">Click to book</button></li>`
} else {
price =element.priceForDisplay.substring(1);
price= parseInt(price);
// console.log(price);
display+= `<li><p>Price: $${price} in total</p></li>`
display+= `<li><button class = "booking-btn">Click to book</button></li>
</ul>
</div>`
// console.log(display);
}});
document.body.innerHTML = display;
}
})
.catch(err => console.error(err));
}
I already tried with localStorage and sessionStorage but as a newbie I am just now sure how to put the whole API data in storage. Also, I desperately tried with window.location object as well but as I assumed that did nothing but open a new tab. Again, thanks in advance for any help!
I am creating a card that shows questions and it makes a GET request to my server and shows the answer to the asked question
The CODE Below of the component shows how the API request is made
const ChatBoxIItem = ({ message }) => {
const [ans, setAns] = useState(null);
getAns(message);
async function getAns(message) {
if (!message) {
return;
}
st_chat_asked_question = message;
let param = {
question: message,
};
param = JSON.stringify(param);
let url;
let res_data;
try {
console.log(param);
let curr_data = new Date();
url = `${process.env.REACT_APP_API_URL}/student/question_answer`;
res_data = await send_xhr.post(param, url, "application/json");
res_data = JSON.parse(res_data);
curr_data = new Date();
let time = curr_data.toLocaleTimeString();
if (
res_data.answers &&
res_data.answers[0] &&
res_data.answers[0].score > 0
) {
st_chat_response_data = res_data;
console.log(res_data);
setAns(res_data.answers[0].answer);
} else {
}
} catch (e) {
display_mess("something went wrong", "error", 5000);
window.scrollTo(0, 100);
console.log(e);
return;
}
}
return (
<div className='st-cs-chat-item'>
<div className='st-cs-chat-item-header'>
<p className='st-cs-chat-item-question'>{message}</p>
<p>{ans ? ans : <p>Searching For Answer</p>}</p>
</div>
</div>
);
};
It's working fine but only for the first component when 2 or more request is made it changes the value of the 1st state also to "Searching for ans"
When 1st request is made
After 1st Request
When 2nd request is made (States of both components changes)
How to fix this issue I don't want to change the states of previous components Need each of them as independent
I am trying to create a script that pulls from the coin market cap API and displays the current price. The script is working fine on the back end when I assign the variable a value. However, when I try to run the function on sheets the returned value is null.
function marketview(ticker) {
var url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?CMC_PRO_API_KEY=XXX&symbol=" + ticker;
var data = UrlFetchApp.fetch(url);
const jsondata = JSON.parse(data);
Logger.log(jsondata.data[ticker].quote['USD'].price)
}
My execution logs show that the scripts are running, but when when I use the function and try and quote ETH for example, the script is running for BTC.
When I do this on the backend and assign ETH the script works fine and returns the right quote. Any ideas on what I'm missing?
I did the same with coingecko API and add an issue having all my requests being rejected with quota exceeded error.
I understood that Google sheets servers IPs address were already spamming coingecko server. (I was obviously not the only one to try this).
This is why I used an external service like apify.com to pull the data and re-expose data over their API.
This is my AppScripts coingecko.gs:
/**
* get latest coingecko market prices dataset
*/
async function GET_COINGECKO_PRICES(key, actor) {
const coinGeckoUrl = `https://api.apify.com/v2/acts/${actor}/runs/last/dataset/items?token=${key}&status=SUCCEEDED`
return ImportJSON(coinGeckoUrl);
}
You need ImportJSON function, available here: https://github.com/bradjasper/ImportJSON/blob/master/ImportJSON.gs
Then in a cell I write: =GET_COINGECKO_PRICES(APIFY_API_KEY,APIFY_COINGECKO_MARKET_PRICES), you will have to create two field named APIFY_API_KEY and APIFY_COINGECKO_MARKET_PRICES in order for this to work.
Then register on apify.com, then you'll have to create an actor by forking apify-webscraper actor.
I set the StartURLs with https://api.coingecko.com/api/v3/coins/list, this will give me the total number of existing crypto (approx 11000 as of today), and number of page so I can run the request concurrently (rate limit is 10 concurrent requests on coingecko), then I just replace /list with /market and set the proper limit to get all the pages I need.
I use the following for the tasks page function:
async function pageFunction(context) {
let marketPrices = [];
const ENABLE_CONCURRENCY_BATCH = true;
const PRICE_CHANGE_PERCENTAGE = ['1h', '24h', '7d'];
const MAX_PAGE_TO_SCRAP = 10;
const MAX_PER_PAGE = 250;
const MAX_CONCURRENCY_BATCH_LIMIT = 10;
await context.WaitFor(5000);
const cryptoList = readJson();
const totalPage = Math.ceil(cryptoList.length / MAX_PER_PAGE);
context.log.info(`[Coingecko total cryptos count: ${cryptoList.length} (${totalPage} pages)]`)
function readJson() {
try {
const preEl = document.querySelector('body > pre');
return JSON.parse(preEl.innerText);
} catch (error) {
throw Error(`Failed to read JSON: ${error.message}`)
}
}
async function loadPage($page) {
try {
const params = {
vs_currency: 'usd',
page: $page,
per_page: MAX_PER_PAGE,
price_change_percentage: PRICE_CHANGE_PERCENTAGE.join(','),
sparkline: true,
}
let pageUrl = `${context.request.url.replace(/\/list$/, '/markets')}?`;
pageUrl += [
`vs_currency=${params.vs_currency}`,
`page=${params.page}`,
`per_page=${params.per_page}`,
`price_change_percentage=${params.price_change_percentage}`,
].join('&');
context.log.info(`GET page ${params.page} URL: ${pageUrl}`);
const page = await fetch(pageUrl).then((response) => response.json());
context.log.info(`Done GET page ${params.page} size ${page.length}`);
marketPrices = [...marketPrices, ...page];
return page
} catch (error) {
throw Error(`Fail to load page ${$page}: ${error.message}`)
}
}
try {
if (ENABLE_CONCURRENCY_BATCH) {
const fetchers = Array.from({ length: totalPage }).map((_, i) => {
const pageIndex = i + 1;
if (pageIndex > MAX_PAGE_TO_SCRAP) {
return null;
}
return () => loadPage(pageIndex);
}).filter(Boolean);
while (fetchers.length) {
await Promise.all(
fetchers.splice(0, MAX_CONCURRENCY_BATCH_LIMIT).map((f) => f())
);
}
} else {
let pageIndex = 1
let page = await loadPage(pageIndex)
while (page.length !== 0 && page <= MAX_PAGE_TO_SCRAP) {
pageIndex += 1
page = await loadPage(pageIndex)
}
}
} catch (error) {
context.log.info(`Fetchers failed: ${error.message}`);
}
context.log.info(`End: Updated ${marketPrices.length} prices for ${cryptoList.length} cryptos`);
const data = marketPrices.sort((a, b) => a.id.toLowerCase() > b.id.toLowerCase() ? 1 : -1);
context.log.info(JSON.stringify(data.find((item) => item.id.toLowerCase() === 'bitcoin')));
function sanitizer(item) {
item.symbol = item.symbol.toUpperCase()
return item;
}
return data.map(sanitizer)
}
I presume you are hiting the same issue I had with coinmarketcap, and that you could do the same with it.
You're not return ing anything to the sheet, but just logging it. Return it:
return jsondata.data[ticker].quote['USD'].price
hope someone can help me.
I'm trying to deploy a web app on netlify, but i don't know how to call the API the right way, providing the right value.
this is my netlify function file:
const fetch = require('node-fetch');
exports.handler = async event => {
const API_KEY = process.env.API_KEY;
const response = await fetch(`https://api.waqi.info/feed/${process.env.CITY}/?token=${API_KEY}`);
const data = await response.json();
const pass = (body) => {
return {
statusCode: 200,
body: JSON.stringify(body)
}
};
return pass(data);
}
My problem is about providing the right city value to the API call.
i've also tried to make city an env var on netlify, but even if i change its value, the file lambda.js provides me always the same value probably because it runs just one time at the start.
Here's the code in index.js:
let CITY = process.env.CITY;
async function getCityPollution(city) {
let response = await fetch("/.netlify/functions/lambda");
let result = await response.json();
if(response.status == 200 && result.status == 'ok') {
await dataHandler(result);
console.log(result);
} else if (result.status == 'error'){
console.log(`${result.status}: ${result.data}`);
setTimeout(() => {
dataParagraph.innerHTML = `Unfortunately we have no datas for ${city} station (incredible but true),
insert coords to check datas from the nearest station or try with another city.
Go check https://waqi.info/ to see our coverage`;
$("html, body").animate({ scrollTop: document.body.scrollHeight }, "slow");
}
, 50);
} else {
console.log(response.status);
}
}
// getting city input and call output function
let getCity = document.querySelector('#getCity');
getCity.onclick = async () => {
CITY = cityInput.value;
if (!CITY) {
emptyFields(cityInput);
} else {
await getCityPollution(CITY);
coordInput[0].value = '';
coordInput[1].value = '';
console.log(CITY) //it works, the value changes
}
}
Obviously this is the try with the netlify env var. This way i get always the same value.
There's a way to pass the right value of CITY everytime i need it? Even without using env variables.
Thank you in advance