so I'm doing a school project where I'm trying to fetch data from my Azure blob-storage and then displaying it on a website which is creating in ReactJS. I've managed to fetch the pictures from my storage and they render fine, but when I try to render the "createdOn prop" nothing is being shown.
I just wanna add that I'm fairly new to javascript so my code might look like garbage.
The BodyPresenter function is the one that connects to my blob storage and then retrieves the data. It works without a problem for the "blobImages" but won't work for the "blobDateTime"
function BodyPresenter() {
const [blobImages, setBlobImages] = useState([]);
const [blobDateTime, setBlobDateTime] = useState([]);
const account = "ktodb";
const containerName = "images";
useEffect(() => {
async function blobStorage() {
const blobServiceClient = new BlobServiceClient(api_client.get_blob_account(account));
const containerClient = blobServiceClient.getContainerClient(containerName);
let blobs = containerClient.listBlobsFlat();
let newArrayForImages = [];
let newArrayforDates = [];
for await (const blob of blobs) {
// console.log(`${blob.name}`); //`Blob ${i++}:
newArrayForImages.push(blob.name);
newArrayforDates.push(blob.properties.createdOn)
}
setBlobImages(newArrayForImages);
setBlobDateTime(newArrayforDates);
}
return blobStorage;
}, [blobImages])
console.log(blobDateTime)
As you can see I console.log() the blobDateTime at the bottom and it gives me the data I want in the console, aka the creation data of all my images:
image of console
Then I try to render the data in a view file and I guess the problem lies here, but I'm sure what the problem is and how to fix it, to remind you I'm a new to javascript. The images are being rendered but not the DateTime
function BodyView( {blobImages, blobDateTime} ) {
console.log(blobDateTime)
return (
<div>
{blobImages.map( (blobImages, blobDateTime) => (
<div>
<div className="images">
<img
key={blobImages.id}
src={api_client.get_image_url(blobImages)}
alt=""
/>
</div>
<div>{blobDateTime}</div>
</div>
))}
</div>
)
}
Related
Currently working with NextJS, but struggling to make an indexing page of sorts. With the router, I'm trying to get the page number by doing this:
let router = useRouter()
let page = isNaN(router.query.page) ? 1 : parseInt(router.query.page);
So that when I go to page=1, page=2 etc, I get new sets of data.
The functionality for this is called in the same main component, and is a React Query function:
const {data, status} = useQuery(["keycaps", {manu: manuId}, {prof: profileId}, {col: colorId}, {stat: statusId}], getKeycaps)
And said function looks like this:
const getKeycaps = async(key) => {
const manuId = key.queryKey[1].manu
const profileIds = key.queryKey[2].prof.map(id => `profile.id=${id}`)
const colorIds = key.queryKey[3].col.map(id => `filter_colors.id=${id}`)
const statId = key.queryKey[4].stat
const profileQueryString = profileIds.join("&")
const colorQueryString = colorIds.join("&")
let urlParams = new URLSearchParams(document.location.search);
let page = urlParams.get("page") == null ? 1 : parseInt(urlParams.get("page"));
let start = (page * 10) - 10
const data = await axios(`
${process.env.REACT_APP_STRAPI_API}/keycaps?${manuId ? 'manufacturer.id=' + manuId + '&' : ''}${profileQueryString ? profileQueryString + '&' : ''}${colorQueryString ? colorQueryString + '&' : ''}_start=${start}&_limit=10`)
return data.data
}
When initially loading pages, like directly pasting the url of the index in (i.e. keycaps?page=2), it will get all the results all fine. However, if I start using navigational buttons like this:
<Link href={`/keycaps?page=${page - 1}`}> // next/link
<div className="w-32 rounded-lg cursor-pointer">
Prev
</div>
</Link>
<Link href={`/keycaps?page=${page + 1}`}>
<div className="w-32 rounded-lg cursor-pointer">
Next
</div>
</Link>
The whole thing starts to break down. Essentially, the page doesn't actually reload any data or results until the page is unfocused. So, if I press the Next button to go to the next page, it won't load the data until I do something like tab to a new window or check a different internet tab, and then when I come back, the data will all magically load within a second.
I've tried this with next build && next start too, and this produces the same results. I just want to get the page results when the next and prev page buttons are pressed, and in a way that doesn't require the user to switch tabs to get content.
I will note that I do have a getStaticProps on this as well. It does the following:
export async function getStaticProps() {
const allKeycaps = (await getAllKeycaps() || 'Error')
return {
props: { allKeycaps }
}
}
Which will call an api script, and said script does this:
async function fetchAPI(query, {variables} = {}) {
const res = await fetch(`${process.env.REACT_APP_STRAPI_API}/graphql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
})
const json = await res.json()
if (json.errors) {
console.error(json.errors)
throw new Error('Failed to fetch API')
}
console.log('json', json.data, variables)
return json.data
}
/* Keycap related grabs */
export async function getAllKeycaps() {
const data = await fetchAPI(
`
{
keycaps {
id
slug
name
published_at
updatedAt
profile {
id
name
}
manufacturer {
id
name
lead
}
status {
id
name
}
colors
filter_colors {
color
}
kits
designer
thumb {
formats
}
}
}
`
)
return data
}
Anyone know how to get this to work? To navigate between indexes like this? I've been trying to look for Next tutorials that use navigations like page 1, page 2 etc but all I can find is examples of blog articles with slugs, no index searches of any kind.
Thanks a lot in advance.
Answer found:
When setting data and status using useQuery
const curPage = router.query.page == null ? 1 : router.query.page
const {data, status} = useQuery(["keycaps", {manu: manuId}, {prof: profileId}, {col: colorId}, {stat: statusId}, {page: curPage}], getKeycaps)
And then, in getKeycaps
const page = key.queryKey[5].page
I guess the "urlParams" approach wasn't a good one? Or at least, not one that was updating quick enough. So passing through the router page number seems to work better.
Let me start with my code, and then will describe my problem:
function ArrayList6(props) {
const title = "Results";
let parseArray = [];
const thisFunc = async () => {
const parseTest = new Parse.Query("Users");
parseTest.equalTo("LastName", "Jenkins");
let queryResults = await parseTest.find();
for (const item of queryResults) {
parseArray.push(item.get('LastName'))
}
console.log("data in function: " + parseArray)
};
thisFunc();
return (
<div className="App">
<NavBar title={title} />
<ul className="no-bullets">
{parseArray.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
);
}
export default ArrayList6;
In short, the problem is that when I fetch my API data Asynchronously, the code keeps running and hits the return portion before the API has fetched the needed data - this is a problem because the returned portions depends on the fetched API data.
If you run the code, you can see that the console displays the fetched data, but the main component does not return it.
I'd be happy to set the API fetch so it's not async, and then the rest of the code waits until it's complete, but I haven't figured out how to do that. If someone who is wiser and smarter than I am, I would greatly appreciate it.
I am trying to map an array of id and fetch relevant data for that id from an external API in React.
Here is my code:
const getDetails = async () => {
ids.map(async(id) => {
const details = await axios.get(`url/${id}`);
setIdDetails(details);
});
};
getDetails();
And trying to display those details in a dropdown menu like this:
{idDetails.map((detail) => {
<MenuItem value={detail}>{detail.data.title}</MenuItem>
})}
I get the error that idDetails.map is not a function. Also, I tried pushing to the array instead of setting state, no luck either. I have this feeling that I am doing the whole data fetching thing wrong. What is the best practice to handle a situation like this?
As mentioned by #SurjeetBhadauriya, use the spread syntax setIdDetails([...idDetails, details]);.
This will push the buffer after spreading it as single items, which will allow you to map them in your jsx.
Just use setIdDetails([...idDetails, details]); to push the new data into the array.
const [idDetails, setIdDetails] = useState([]);
const getDetails = async () => {
ids.map(async(id) => {
const details = await axios.get(`url/${id}`);
setIdDetails([...idDetails, details]);
});
};
getDetails();
Also, make this change to avoid data specific error
{(idDetails || []).map((detail) => {
<MenuItem value={detail}>{detail?.data?.title}</MenuItem>
})}
Note: I am hoping that you are getting details as an object here const details = await axios.get(url/${id});
How do I use setState to render individual data returned from API call. As the question implies... What I am getting instead is another array of data.
Here's the code:
const [likes, setLikes] = useState(0);
useEffect( async () => {
const query = await getDevsApi(); //This returns a collection of data I can map.
const likes = query.map(like => like.upvotes); //The problem here is, it returns an array of data... How do I get individual data for each user? and set it at as the current state in the setLikes() below.
setLikes(likes)
},[]);
I am still wrapping my head around reactjs. Can anyone explain the best approach for me?
How it looks...
UPDATED CODE
ver. 1.
const [likes, setLikes] = useState(0);
useEffect(() => {
//using an anonymous async function inside a useEffect
( async () => {
const query = await getDevsApi();
setLikes(query.map(like => like.upvotes));
})
(); //calling the anonymous function.
},[]);
ver. 2.
const [likes, setLikes] = useState(0);
useEffect(() => {
( async () => {
const query = await getDevsApi();
const likes = query.map(like => like.upvotes);
setLikes(likes);
})
();
},[]);
However, they both produce the same result.
The idea is to get the number of likes from the API and display them in this section
<span className={styles.iconLeft}>
<a href="#" onClick={addLike} ><small>{likes} Likes <FontAwesomeIcon className={styles.columnIcon} icon={faHeart} /></small> </a>
</span>
The {likes} is where the magic happens. The initial state was set to 0... and the useEffect will be used to display the real data gotten from the API. And Yes, I got the data BUT it's not distributed to each user as supposed, rather it's giving me an array and appending it to all the user.
See the screenshot above.
I hope I'm able to communicate clearly though.
I just pushed my project to vercel.
here is a branch of what I am having now... https://naijadev-4g9llmolb-badt0men.vercel.app/
And here is another branch of how I want it... https://naijadev-five.vercel.app
And yes this is my very first attempt working with Jamstack... using the now popular headless CMS (strapi)
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array. see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
But this won't be an issue. if you want to render individual data from an array you should use the map() method in the return block of a functional component where UI is rendered.
const [likes, setLikes] = useState([]);
useEffect( async () => {
const query = await getDevsApi();
const likes = query.map(like => like.upvotes);
setLikes(likes)
},[]);
return (
...
<span className={styles.iconLeft}>
{ likes?.map(like => <a href="#" onClick={addLike} ><small>{like} Likes
<FontAwesomeIcon className={styles.columnIcon} icon={faHeart} /></small> </a>
}
</span>
)
I'm generating PDF by using https://pdfgeneratorapi.com/.
Now I can show data one by one using this code.Can any one give me suggestion how can show all data with loop or any other way?
This below photos showing my template from pdfgenerator .
This is the code I'm using to generate PDF
let communicationWay1=[
{0:"dim"},
{1:"kal"}
];
let cstomerExpence1=[
{0:"dim"},
{1:"kal"}
];
let title="test";
let names="test";
let phone="test";
let email="test";
let maritalStatus="test";
let city="test";
let other="test";
const result = await wixData.query(collection)
.eq('main_user_email', $w('#mainE').text)
.find()
.then( (results) => {
if (results.totalCount>0) {
count=1;
// title=results.items[1].title;
names=results.items[0].names;
email=results.items[0].emial;
phone=results.items[0].phone;
maritalStatus=results.items[0].maritalStatus;
city=results.items[0].city;
other=results.items[0].cousterExpenses_other;
title=results.items[0].title;
communicationWay=results.items[0].communicationWay;
cstomerExpence=results.items[0].cstomerExpence;
}
if (results.totalCount>1) {
names1=results.items[1].names;
email1=results.items[1].emial;
phone1=results.items[1].phone;
maritalStatus1=results.items[1].maritalStatus;
city1=results.items[1].city;
other1=results.items[1].cousterExpenses_other;
title1=results.items[1].title;
communicationWay1=results.items[1].communicationWay;
cstomerExpence1=results.items[1].cstomerExpence;
}
} )
.catch( (err) => {
console.log(err);
} );
// Add your code for this event here:
const pdfUrl = await getPdfUrl
({title,names,email,phone,city,maritalStatus,other,communicationWay,cstomerExpence,title1,
names1,email1,phone1,city1,maritalStatus1,other1,communicationWay1,cstomerExpence1
});
if (count===0) { $w("#text21").show();}
else{ $w("#downloadButton").link=wixLocation.to(pdfUrl);}
BELOW CODE IS BACKEND CODE/JSW CODE.
Also I want to open pdf in new tab. I know "_blank" method can be used to open a new tab.But I'm not sure how to add it with the url
import PDFGeneratorAPI from 'pdf-generator-api'
const apiKey = 'MYKEY';
const apiSecret = 'MYAPISECRET';
const baseUrl = 'https://us1.pdfgeneratorapi.com/api/v3/';
const workspace = "HELLO#gmail.com";
const templateID = "MYTEMPLATEID";
let Client = new PDFGeneratorAPI(apiKey, apiSecret)
Client.setBaseUrl(baseUrl)
Client.setWorkspace(workspace)
export async function getPdfUrl(data) {
const {response} = await Client.output(templateID, data, undefined, undefined, {output: 'url'})
return response
}
Just put it in a while loop with a boolean condition.
You can create a variable, for example allShowed, and set its value to False. After that, create another variable, for example numberOfDataToShow, and set it as the number of elements you want to display. Then create a counter, countShowed, initialized with 0 as its value.
Now create a while loop: while allShowed value is False, you loop (and add data).
Everytime a piece of your data is showed, you increment the value of countShowed (and set it to go on adding/showing data). When countShowed will have the exact same value of numberOfDataToShow, set allShowed to True. The loop will interrupt and all your data will be showed.
You would need to use the Container or Table component in PDF Generator API to iterate over a list of items. As #JustCallMeA said you need to send an array of items. PDF Generator API now has an official Wix Velo (previously Corvid) tutorial with a demo page: https://support.pdfgeneratorapi.com/en/article/how-to-integrate-with-wix-velo-13s8135