React, how to render static json data with different keys - arrays

Here's an example of the static json data.
I'm having trouble rendering the 'promoimage' from the array.
I'm not 100% sure how to go about this to solve it. I was playing arround and wanted to check if 'promoimage' exists, but nothing was returned?
Any advice how to achieve this?
[
{
"title": "some title",
"promoimage": "image.jpg",
"url": "#"
},
{
"title": "some title",
"image": "example.jpg",
"url": "#"
},
{
"title": "some other title",
"promoimage": "image.jpg",
"url": "#"
},
{
"title": "title",
"image": "example.jpg",
"url": "#"
},
]
My React component:
import products from '../product-data.json';
...
export const CustomSlider = () => {
// Here I'm using react-slick
const productList = products.map((product, i) => {
const uniqueItems = [];
if (uniqueItems.indexOf(product.imageone) === -1) {
uniqueItems.push(product.imageone);
}
/* This works
if (product.hasOwnProperty('promoimage')) {
return true
}
*/
return <Product key={i} {...product} />;
}
);
return (
<Slider>
{productList}
</Slider>
)
}

The code is sending all object keys to Product, as props. Particularly this part {...product} is expanded into this:
<Product
key={i}
title="some title"
promoimage="image.jpg"
url="#"
/>
This is called spreading.
Now, I suspect <Product> doesn't know what to do with promoimage, but knows what to do with image. We haven't sent any image so we have to fix that. We can do so by either modifying product so that it renders image || promoimage, or change our parsing to this:
const productList = products.map((product, i) => {
const uniqueItems = []
if (uniqueItems.indexOf(product.promoimage) === -1) {
uniqueItems.push(product.promoimage)
}
return (
<Product
key={i}
{...product}
image={product.image || product.promoimage}
/>
)
})

Related

filter array in props

i keep having a problem with filtering an array of objects in props in a nextjs page. i m using below json and coding.
[
{
"id": "1",
"name": "name1",
"category": ["food"]
},
{
"id": "2",
"name": "name2",
"category": ["food", "beverages"]
}]
import React from "react";
const test2 = ({ prods }) => {
return (
<div>
<div>
{prods
.filter((product) => product.category.includes("eve"))
.map((filterarray) => (
<li>
{filterarray.id}
{filterarray.name}
{filterarray.category}
</li>
))}
</div>
</div>
);
};
export async function getStaticProps() {
const prods = (await import("./product.json")).default;
return {
props: {
prods,
},
};
}
export default test2;
listing the full array works. (data comes as 1 string eg 'foodbeverage' but that should still be ok i think)
Filtering on id works fine. But when i try to use include it no longer displays any result.
if anyone could point me to what i m doing wrong. or if i better follow a different approach, any help would be much appreciated.
You need to replace include with some on the category array:
let prods = [
{
id: "1",
name: "name1",
category: ["food"]
},
{
id: "2",
name: "name2",
category: ["food", "beverages"]
}
];
let result = prods.filter((product) =>
product.category.some((pro) => pro.includes("eve"))
);
console.log(result);
Array#includes() does not do partial matches. You need to use String#includes() on each element in the array.
You could do this by using Array#some() in the filter.
prods.filter((product) => product.category.some(cat=>cat.includes("eve")))

star wars api, how do I get values from api?

I'm using the https://swapi.dev/api/people/ and I'm having an interesting issue with an api I haven't encountered before. Here is an example response of the first value form the api using https://swapi.dev/api/people/
[
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "http://swapi.dev/api/planets/1/",
"films": [
"http://swapi.dev/api/films/1/",
"http://swapi.dev/api/films/2/",
"http://swapi.dev/api/films/3/",
"http://swapi.dev/api/films/6/"
],
"species": [],
"vehicles": [
"http://swapi.dev/api/vehicles/14/",
"http://swapi.dev/api/vehicles/30/"
],
"starships": [
"http://swapi.dev/api/starships/12/",
"http://swapi.dev/api/starships/22/"
],
"created": "2014-12-09T13:50:51.644000Z",
"edited": "2014-12-20T21:17:56.891000Z",
"url": "http://swapi.dev/api/people/1/"
},
]
The value I want to get is the first value of the films array, which I can achieve however its just a url. I'd need to somehow grab that url, make an api call with it and then get the first film. An example response after calling the api for the first value of films looks like this:
{
"title": "The Empire Strikes Back",
"episode_id": 5,
"opening_crawl": "It is a dark time for the\r\nRebellion. Although the Death\r\nStar has been destroyed,\r\nImperial troops have driven the\r\nRebel forces from their hidden\r\nbase and pursued them across\r\nthe galaxy.\r\n\r\nEvading the dreaded Imperial\r\nStarfleet, a group of freedom\r\nfighters led by Luke Skywalker\r\nhas established a new secret\r\nbase on the remote ice world\r\nof Hoth.\r\n\r\nThe evil lord Darth Vader,\r\nobsessed with finding young\r\nSkywalker, has dispatched\r\nthousands of remote probes into\r\nthe far reaches of space....",
"director": "Irvin Kershner",
"producer": "Gary Kurtz, Rick McCallum",
"release_date": "1980-05-17",
}
I've never used an api that has urls as values, any ideas on how this might work?
Here is my current code:
Page.jsx
import React, {useState, useEffect} from 'react';
import '../css/Page.css';
import axios from 'axios';
const Page = () => {
const [data, setData] = useState([]);
const fetchData = async () => {
try {
const res = await axios.get(
"https://swapi.dev/api/people/"
);
setData(res?.data.results);
} catch (error) {
console.log(error);
}
}
useEffect(() => {
fetchData();
}, [])
const mapData = () => {
if(data && data.length > 0) {
return (
<>
{data.map(item => (
<div className="itemContainer" key={item.name}>
<div className="image"><img src="" alt=""/></div>
<div className="content">
<span className="title">Name: </span><span className="name">{item.name}</span>
<br />
<span className="title">Birthyear: </span><span className="name">{item.birth_year}</span>
<br />
<span className="title">Homeworld: </span><span className="name">{item.homeworld}</span>
</div>
</div>
))}
</>
)
}
}
return (
<div className="container">
{mapData()}
</div>
);
}
export default Page;

Mapping API response to JSX

I have a JSON response in the form:
[
{
"author": 2,
"title": "how to draw",
"slug": "how-to-draw",
"content": "second attempt",
"status": 0,
"date_created": "2020-11-28T20:25:41.650172Z",
"date_posted": "2020-11-28T20:25:41.650172Z",
"tags": "none"
},
{
"author": 1,
"title": "Admin test post",
"slug": "admin-test-post",
"content": "This is just a test of the content field!\r\n\r\nParagraph 2",
"status": 4,
"date_created": "2020-11-16T21:02:02.521705Z",
"date_posted": "2020-11-16T21:37:40.477318Z",
"tags": "a b c"
}
]
And I am struggling to map the response correctly to a series of divs in ReactJS. The idea would be to have a div for each post, each with the author, title, etc. of the post. I can use post[0].title to take the title of the first element of the array. Am I missing something obvious?
import React, {useState} from 'react';
import axios from 'axios';
const Django = () => {
const [posts, setPosts] = useState([]);
// proxy set to http://127.0.0.1:8000/
const apiLink = "/api/post/";
const fetchData = async () => {
const res = await axios.get(`${apiLink}`, { headers: { Accept: "application/json"} });
console.log(res.data);
setPosts([res.data]);
}
return (
<div>
<h1>Posts:</h1>
<button onClick={fetchData}>Load jokes</button>
{posts.map((post, key) => {
//console.log(joke);
return (
<div className="data" key={key}>
{post.title}
{key}
</div>
)})}
</div>
);
}
export default Django;
It is not exactly clear what your problem is, but I suppose you're confused about why you aren't getting any data by directly calling {post.title} in the map body. That's because you're putting the result from your fetch, which is already an array into another array: setPosts([res.data]);. Just change the state setter to setPosts(res.data); and everything in the map should work fine.

how do I Mock API and following the same approach as my static array?

I have a React hooks component, which uses an HTML div-alike table to render data, and the data is being fetched from the server. I need to test the component to see if the table has data by mocking the API call. Below is the current code. I want to remove the use of arr completely and make avoid getting {users} rendered as text/plain as you can you in the below image
const arr = [
{
"demo": [
{
"_id": "T0810",
"title": "Historian",
"tags": [
"demo"
],
"queries": [],
},
{
"_id": "T0817",
"title": "book",
"tags": [
"demo"
],
"queries": [],
},
],
"demo_2": [
{
"_id": "T0875",
"title": "Program",
"tags": [
"demo_2",
"Control"
],
"queries": [],
},
{
"_id": "T0807",
"title": "Interface",
"tags": [
"demo_2"
],
"queries": [],
}
]
}];
const keys = Object.keys(arr[0]);
export default function Demo () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const handleOnClick = async () => {
try {
const { data } = await axios.get('https://run.mocky.io/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e');
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
return (
<div className="container">
<>
{keys.map((key) => (
<div className="col" key={key}>
<div className="row">{key}</div>
{arr[0][key].map((item) => (
<div className="row" key={item.technique_id} onClick={() => handleOnClick(item)}>{item.technique}</div>
))}
</div>
))}
</>
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/>}
</div>
);
}
Second attempt...Assuming I understand the ask then a package like Nock can do the trick (https://github.com/nock/nock).
The approach is to allow Nock to mock the API and return the result locally without calling the server. In your case it will be something like:
const nock = require('nock');
const arr = [...]; // your original definition of arr
const scope = nock('https://run.mocky.io')
.get('/v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e')
.reply(200, arr);
// The rest of your code "as is"
...
This setup of Nock will intercept every HTTP call to https://run.mocky.io and will specifically return the content of arr in response to a GET /v3/0d7aa6e3-fc01-4a47-893d-7e1cc3013d4e.
Typically, I would not put this code in the main code.
Instead you can use it as part of a testing script that runs without dependency on an active server. E.g. testing during build.

How to run map function in Reactjs?

How to run map function I am trying to map function but it throws error cannot read the property of map undefined in Reactjs
I'm trying to do something like the following in React JSX component
const data = {
Details: [
{
"id": "6f12",
"nextPart": {
"id": "1ae2",
"text": "Details",
"heading": "twice data and staff memeber",
"checks": [
{
"value": "A"
},
{
"value": "B"
}
],
"Types": "Error"
},
"conclusion": "final"
}
]
}
The nextPart property is an object, not an array. Map works on arrays.
If your Details is intended to be an array of objects (which looks like it does), you can do it like this
{data.Details.map((item) => {
const { text, heading, checks } = item.nextPart;
return (
<div>
<div>{text}</div>
<div>{heading}</div>
<div>
{checks.map(({ value }) => (
<div>{value}</div>
))}
</div>
</div>
);
})}

Resources