I'm trying to show a page in React that shows a grid of images. I get the data for the grid with a fetch to a file in a public subfolder.
export const GalleryGrid = () => {
const { galleries, loading } = useFetchGalleries()
return (
<div>
{loading && <div className="diving__loading flex-column">
<div><Loader
type="Puff"
color="#264653ff"
height={200}
width={200}
/></div>
<div>
<p>
Please Wait ...
</p>
</div>
</div>}
<div className="card-grid">
<div className="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-5">
{
galleries.map(gal => (
<GalleryGridItem
key={gal.url}
{...gal}
/>
))
}
</div>
</div>
</div>
)
}
I use the function "useFetchGAlleries()" to get the data I neew for the grid. This function launch a helper, called "getGalleries", and return de data stored with useState
export const useFetchGalleries = () => {
const [ state, setState] = useState({
galleries: [],
loading: true
});
useEffect(() => {
getGalleries()
.then( galleries => {
setState({
galleries,
loading: false
})
})
.catch( e => {
console.log( e );
});
}, [ ]);
return state;
}
The helper "getGalleries" uses a fetch to read the files that contain objects in json format, and transform them to an object:
export const getGalleries = async () => {
const galleries = await fetch(`./assets/data/galleries.txt`)
.then( res => {
return res.json();
})
.then( body => {
return body.galleries;
})
.catch( e => {
console.log( e );
});
// the .map check if the gallery have images or not
await galleries.map( gal => (
gal.interactive ? fetch(`./assets/data/${gal.url}.txt`)
.then( res => {
return res.json();
})
.then( body => {
gal.images = body.images;
})
.catch( e => {
console.log( e );
})
: ""
));
return galleries ;
}
In "GalleryGrid" I have all information about galleries I need. For example, the first gallery from the array is:
{
"name": "Nuestros Fondos 2022",
"url": "galeria_22_fishes",
"number": 8,
"interactive": true,
"images": [
{"index": '01', "url": 'galeria_03_cursos'},
{"index": '02', "url": 'galeria_03_cursos'},
{"index": '03', "url": 'galeria_03_cursos'},
{"index": '04', "url": 'galeria_03_cursos'},
{"index": '05', "url": 'galeria_03_cursos'},
{"index": '06', "url": 'galeria_03_cursos'},
{"index": '07', "url": 'galeria_03_cursos'},
]
}
But when I give the info to the component "GalleryGridItem" in the "GalleryGrid" function, the data doesn't contain the array of images. The data is as follow:
{
"name": "Nuestros Fondos 2022",
"url": "galeria_22_fishes",
"number": 8,
"interactive": true,
"images": []
}
Any suggestion about what is happening?
The JSON of images is invalid JSON. You need to add quotes to the keys.
You are awaiting an array of promises. You need to use Promise.all()
await Promise.all(galleries.map( gal => (...))
Your function is returning immediately after .map, before any images are loaded.
Related
I have a component to render only the latest product gets from API:
const about = ({products}) => {
const data = products.attributes
console.log(data)
return (
<div>
<h1>{data.Name}</h1>
<p>{data.Description}</p>
<p>{Number(data.Price).toLocaleString('it-IT', {style : 'currency', currency : 'VND'})}</p>
<p>{data.Release}</p>
<p>{data.Expire}</p>
<p>{data.Close ? "Close" : "Open"}</p>
</div>
);
}
export async function getStaticProps() {
const data = await fetch(myAPI)
const res = await data.json()
const products = res.data[0]
return {
props: {products}
}
}
export default about;
The JSON from API looks like this:
{
"data": [
{
"id": 1,
"attributes": {
"Name": "Vĩ Hoạ",
"Description": "Vĩ Hoạ",
"Price": "30000",
"Release": "2022-05-04",
"Expire": "2022-05-26",
"Close": false,
"createdAt": "2022-05-09T22:28:09.622Z",
"updatedAt": "2022-05-10T05:50:38.430Z",
"publishedAt": "2022-05-10T05:50:12.353Z"
}
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 1
}
}
}
The highest id from JSON is the latest product, I haven't figured out how to resolve this JSON for getting the latest product.const products = res.data[x] is used to get the specific product based on x, like an index. This is my temporary solution but not flexible to get the latest one!
The best solution is descending data by createdAt when getting data from the API. Otherwise, you can use this method.
const about = ({products}) => {
const data = products.attributes
console.log(data)
return (
<div>
<h1>{data.Name}</h1>
<p>{data.Description}</p>
<p>{Number(data.Price).toLocaleString('it-IT', {style : 'currency', currency : 'VND'})}</p>
<p>{data.Release}</p>
<p>{data.Expire}</p>
<p>{data.Close ? "Close" : "Open"}</p>
</div>
);
}
export async function getStaticProps() {
const data = await fetch(myAPI)
const res = await data.json()
const products = res.data.sort((a, b) => b.id - a.id)[0]
return {
props: {products}
}
}
export default about;
I am trying to add a "sorting" system to a clothing website I am building. The issue I am having is that whenever a new parameter is being added, it removes the old one added. I would guess the reason is that the variable holding the parameters are being re-rendered whenever you sort the products.
Here is my code:
const FetchAPI = (props) => {
const [product, setProducts] = useState([]);
// Key and Value
let facetKey = props.facetKey;
let facetValue = props.facetValue;
let params = {
store: "US",
offset: props.offset,
categoryId: props.categoryId,
limit: props.limit,
country: "US",
sort: "freshness",
currency: "USD",
sizeSchema: "US",
lang: "en-US",
};
if (facetKey) {
params = { ...params, offset: 0, limit: 0, [facetKey]: facetValue };
}
useEffect(() => {
const options = {
method: "GET",
url: "https://asos2.p.rapidapi.com/products/v2/list",
params: params,
headers: {
"x-rapidapi-key": "",
"x-rapidapi-host": "",
},
};
axios
.request(options)
.then(function (response) {
setProducts(response.data.products);
props.items(response.data.itemCount);
props.facets(response.data.facets);
})
.catch(function (error) {
console.error(error);
});
}, [props.limit, facetValue]);
return (
<div>
<div className={classes.container}>
{product.map((product) => (
<ProductCard
key={product.id}
img={product.imageUrl}
name={product.name}
price={product.price.current.text}
/>
))}
</div>
</div>
);
};
The re-rendering of params occurs because it is inside of the const FetchAPI, but I am not that sure how I can "ignore" that and make the params keep the first value. Perhaps could I solve this by putting the values in localstorage? Or is there a better way?
How do I set my returned data from a JSON file to a const so I can use it in other functions. I'm able to console.log but how do I proceed? The end goal is to be able to use data.purchase_orders and loop through the data (ie - price_list)
data.json
{
"purchase_order": [
{
"id": "1",
"external_number": "1000",
"status": "Created",
"price_list": [
{
"id": "msrp",
"name": "retail price",
"currency": "USD"
}
],
"shipments": [
{
"id": "1",
"external_number": "10000",
"status": "Created",
"tracking_number": "Z1F2"
},
{
"id": "2",
"external_number": "9000",
"status": "In Transit",
"tracking_number": "PL21F"
}
]
}
]
}
index.html
<div id="shipments"></div>
<script>
const data_file = 'data.json';
async function fetchPO(){
const reponse = await fetch(data_file);
const data = await response.json();
const PO = data.purchase_orders.forEach((PODetails) => {
console.log(PODetails);
//^this displays key and value of my JSON data_file
})
displayShipments(PO);
}
fetchPO();
function displayShipments(shipmentsList){
document.getElementById("shipments").innerHTML = `
${Object.keys(shipmentsList).map(function (shipments)
return `${shipments.id}
<p>${shipments.tracking_number}</p>`
)}
}
`
}
</script>
One approach is to return the data from the async fetccPO() and store the promise in a variable.
A promise can be used as many times as you want.
Simplified example:
async function fetchUsers() {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
return response.json();
}
// makes request and stores promise in variable
const usersPromise = fetchUsers();
async function logAddress() {
// promise can be used many times
const users = await usersPromise;
console.clear()
console.log(users[0].address);
}
async function logUserName() {
const users = await usersPromise;
console.clear()
console.log(users[0].name);
}
<button onclick="logAddress()">Log first user address</button>
<button onclick="logUserName()">Log first user name</button>
I'd like to get my trips, which are my response from API in Angular.
From the backend I'm getting:
{
"trips": [
{
"id": 0,
"name": "string",
"startDate": "2019-06-30T06:05:48.006Z",
"endDate": "2019-06-30T06:05:48.006Z",
"description": "string",
"roomSharing": true,
"countries": [
{
"id": 0,
"name": "string",
"code": "string"
}
],
"languages": [
{
"id": 0,
"name": "string",
"code": "string"
}
]
}
]
}
which is fine, but I have a problem on the client side.
Here's my code for getting trips:
getTrips(): Observable<Trip[]> {
return this.http.get<Trip[]>(this.apiUrl + '/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map(data => {
return data;
})
);
}
and in my component I have:
loadTrips() {
return this.rest.getTrips()
.subscribe((data) => {
this.trips = data;
console.log(this.trips);
}
, err => console.log(err));
}
I'd like to get trips in a template like:
<div class="card mb-3 trip" *ngFor="let trip of trips">
but I have to like:
<div class="card mb-3 trip" *ngFor="let trip of trips.trips">
So, the question is how can I map my response to get Trip array instead of Array of Trips array?
Unless I'm misunderstanding something, this should work:
interface TripsResponse {
trips: Trips[],
}
getTrips(): Observable<Trip[]> {
// use your response interface instead
//return this.http.get<Trip[]>(this.apiUrl + '/Trip/Get')
return this.http.get<TripsResponse>(this.apiUrl + '/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map(data => {
return data.trips; // set it properly here
})
);
}
Change your return statement:
return this.http.get('/Trip/Get')
.pipe(
tap(_ => console.log('fetched trips')),
retry(1),
catchError(this.handleError),
map((data: TripsResponse) => { // change made here; make data of type TripsResponse
return data.trips;
})
);
where TripsResponse is
interface TripsResponse {
trips: Trips[],
... // other fields for future if required
}
Dont over complicate by doing .map, just do:
loadTrips() {
return this.rest.getTrips()
.subscribe((data) => {
this.trips = data.trips;
}
, err => console.log(err));
}
Also, correct the model Trip[] which you have created it should be
export interface ITripsResponse {
trips: Trips[],
}
return this.http.get<ITripsResponse>(this.apiUrl + '/Trip/Get')
or else, correct .map by
map((data) => {
return data.trips;
})
and then Observable<Trip[]> would be a valid return type
I am trying to parse a nested json request from my reactjs web app.
Below is the json that I received from a request.
response.data
{
"total": 2,
"offset": 1,
"limit": 987,
"staging": [
{
"id": 101,
"name": "Test Stage"
},
{
"id": 102,
"name": "Dev Stage"
},
{
"id": 103,
"name": "Prod Stage"
}
]
}
I need to parse “staging” and display the results on browser screen.
Below is the code that I am trying to parse. But, it is throwing error (SyntaxError: Unexpected token o in JSON at position 1).
export default class ItemLister extends React.Component {
state = {
persons: []
}
componentDidMount() {
axios
.get('https://xxx.yyy.zzz/xyz/zyx/', {
headers: {
'authorization':'Bearer XXXXXXXXX',
'X-Api-Key': 'XXXXXXXXXXXXXX',
},
withCredentials: true
})
.then(response => {
console.log(response.data) // it gets the correct response and printing in logs
const persons = response.data;
this.setState({ persons });
})
.catch (err => {
console.log("error")
});
}
render() {
return <ul>{this.state.persons.map(person => <li>{person.name}</li>)}</ul>
}
}
ReactDOM.render(<ItemLister />, document.getElementById('root'))
registerServiceWorker()
I couldn't find fix for it. Can someone guide me whether the parsing of such json is correct or not and how to get the parsed results and displayed on screen?
An error occurs because you're trying to parse an Object instead of a String. Simply skip JSON.parse and set result to response.data:
.then(response => {
console.log(response.data) // it gets the correct response and printing in logs
this.setState({ result: response.data });
})
And in you render:
render() {
return (
<ul>
{ this.state.result &&
this.state.result.staging &&
this.state.result.staging.map(person => <li>{person.name}</li>)
}
</ul>
);
}