How can I loop over a JSON object in React - javascript

I'm trying to retrieve information from the following JSON object data.json:
{
"status": "ok",
"feed": {
"title": "NOS Nieuws",
},
"items": [
{
"title": "Test Title",
"description": "Test description",
"enclosure": {
"link": "https://examplelink.com/1008x567.jpg",
"type": "image/jpeg"
},
"categories": []
},
{
"title": "Test Title 2",
"description": "Test 2",
"enclosure": {
"link": "link": "https://examplelink.com/1008x567.jpg",
"type": "image/jpeg"
},
"categories": []
}
]
}
So I want to loop over this JSON object to display all the available items and its corresponding title, description and enclosure-link.
I know i can access them separately as:
const items = data.items;
const title = items.title;
const url = items.enclosure.link;
Usually I would do a for-loop and loop through data.items[i]. However, since this is a react and an object instead of an array it works differently.
My current code:
class Feed extends Component {
render() {
const items = data.items[0];
const title = items.title;
const url = items.enclosure.link;
const description = items.description;
const feed = [
{
url: url,
title: title,
description: description
}
];
return (
<div className="feed">
<h1>Newsfeed</h1>
<div className="columns is-multiline">
{feed.map(article => (
<div className="column is-one-third">
<NewsCard
article={article}
title={items.title}
description={items.description}
/>
</div>
))}
</div>
</div>
);
}
}
Right now its only displaying the first entry of the object (because it has const items = data.items[0]) How can I loop over data.json and display its content in the NewsCard component? I know that each child should have a unique 'key' prop but thats where I'm stuck.

I want to loop over this JSON object to display all the available
items and its corresponding title, description and enclosure-link
Then instead of doing this:
const items = data.items[0];
Try this:
const items = data.items;
Then, you can use the map function, like this:
items.map(item => (
<div className="column is-one-third">
<NewsCard
article={item.enclosure.link}
title={item.title}
description={item.description}
/>
</div>
));

You could do something like this.
class Feed extends Component {
render() {
let newsCards = data.items.map(item => {
<div className="column is-one-third">
<NewsCard
article={item}
title={item.title}
description={item.description}
/>
</div>
});
return (
<div className="feed">
<h1>Newsfeed</h1>
<div className="columns is-multiline">
{newsCards}
</div>
</div>
);
}
}

const feed = data.items
{feed.map(item => (
<div className="column is-one-third">
<NewsCard
article={item.enclosure.link}
title={item.title}
description={item.description}
/>
</div>
))}
try this way

Related

Avoid unnecessary component rendering with memo in nextjs

I'am trying to understand react's behaviour throught nextjs.
I have an index.js page with one component Homecard displayed three times and one button that increment a value.
Each time I click on button all Homecard components are re-render.
index.js
import { Homecard } from '../components/Homecard'
import { useState } from 'react'
export default function Home() {
const [count, increment] = useState(0);
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
const handleCount = () => {
increment(count => count + 1);
}
return (
<>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</>
)
}
homecard.js
export default function Homecard({ main, sub, desc, nav }) {
console.log('render Homecard');
return (
<div className={`${styles.homecard}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<Link href={link.href}>
<a>{link.value}</a>
</Link>
</li>
))}
</ul>
}
</div>
</div>
)
}
I tried to wrap my Homecard with React.memo like so
const Homecard = React.memo(({ main, sub, desc, nav }) => {})
But I still see console.log('render Homecard'); when my button is clicked.
How can I could update only my button and not others components ?
The problem is that you're recreating your homecards array on every render of Home, so each nav object is a new object, and so React.memo sees a difference in the props and doesn't optimize away the subsequent re-renders.
There are a couple of ways to fix it:
Define homecards outside Home; it's unchanging, so there's no reason to recreate it every time Home is called.
If you couldn't do that for some reason, you could pass a second argument to React.memo, the "areEqual" function, which will receive the old props and the new ones; in the function, do a deep comparison to see if if nav objects have the same property values are the previous ones even though they're different objects.
Example of #1:
const { useState } = React;
/*export default*/ const Homecard = React.memo(({ img, main, sub, desc, nav }) => {
console.log('render Homecard');
return (
<div className={`${""/*styles.homecard*/}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<a href={link.href}>
{link.value}
</a>
</li>
))}
</ul>
}
</div>
</div>
)
});
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
/*export default*/ function Home() {
const [count, increment] = useState(0);
const handleCount = () => {
increment(count => count + 1);
}
return (
// Sadly, Stack Snippets don't support <>...</>
<React.Fragment>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</React.Fragment>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Home />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
(Warning: I changed a couple of minor things to make this work in Stack Snippets, like replacing Link and using React.Fragment instead of <>...</>, so don't copy and paste it directly back into your project, just move homecards in your existing code.)

map function not showing elements on screen

i have this part of code the map function did not show any element of the array, if i console.log the variable it shows me the elements but for some reasons i can't show the elements on the screen.
Code
function Solution({list}){
const data = list
console.log(data);
return(
<div>
{
data?.map((item) => {
return (
<div>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export default Solution;
const list = [
{
title: "Home"
},
{
title: "Service",
subItem: ["Clean", "Cook"]
},
{
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
Solution({list})
Please, just pass "list" link this.
<Solution list={list}/>
Hope will help you, Thanks)
Check this out
import React from 'react';
function Solution({list}){
const data = list
console.log(list);
return(
<div>
{
data?.map((item) => {
return (
<div key={item.id}>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export function App(props) {
const list = [
{
id:1,
title: "Home"
},
{
id:2,
title: "Service",
subItem: ["Clean", "Cook"]
},
{
id:3,
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
return (
<div className='App'>
<Solution list={list} />
</div>
);
}
// Log to console
console.log('Hello console')
Have a unique key prop for each element when you map an array and send list array as props to your Solution component

iterating through an array of objects and displaying the items [REACT JS]

I'm trying to iterate through an array of objects, displaying the results inside divs but something is not working as intended. When I console log it seems to retrieve the data and show it.
const example =
{
"example": [
{
"test": "test",
"img": "img.png",
"song": "song title"
},
{
"test": "test2",
"img": "img.png2",
"song": "song title2"
}
]
}
const renderData= () => {
example.example.forEach(function (arrayItem) {
const test= arrayItem.test
const img= arrayItem.img
const song= arrayItem.song
return (
<div className="test">
<div className="test">
<div className="test">
<img
src={img}
alt="sunil"
/>
</div>
<div className="test">
{test}
<span className="test">
</span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};
return (
<div
{renderData()}
</div>
);
}
nothing really shows up, but when i do:
example.example.forEach(function (arrayItem) {
var x = arrayItem.test+ arrayItem.img+ arrayItem.song;
console.log(x);
});
it works and consoles the right info.
Can anyone spot the mistake or help out?
Please ignore the naming convention.
You need return array of JSX.Element from renderData. In your case you return undefined. Return a new array of JSX.Element with map instead forEach, which returns nothing.
const renderData = () => {
return example.example.map((arrayItem, i) => {
const test = arrayItem.test;
const img = arrayItem.img;
const song = arrayItem.song;
return (
<div key={i} className="test">
<div className="test">
<div className="test">
<img src={img} alt="sunil" />
</div>
<div className="test">
{test}
<span className="test"></span>
<p>{song}</p>
</div>
</div>
</div>
);
});
};

React JS - data.map can't create list with the data

I got data.js file with some basic info for food like:
export default [
{
"name": "Crock Pot Roast",
"information":[
{
"date":"24 July 2019",
"type": "Main dish",
"difficulty": "Easy",
"time": "~50",
}
],
"ingredients": [
{
"quantity": "1",
"name": " beef roast",
"type": "Meat"
}
],
...
Some more data
...
}
]
I want to create list on react that can get all elements from data.information which function will be correct to use?
I'm getting the data like this:
const getData = data.map(food => {
return (
<div key={food.name}>
<span>{food.name}</span>
<div>
<div>
<img src={food.imageURL} alt={food.name} />
<div>
<ul>{getFoodInformation}</ul>
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
});
but I can't use food.information.map to create list like:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
Any ideas maybe to use another function not map?
You're right that this line is the problem:
<ul>{food.information.map((info) => <div key={info.date}>{info}</div>) }</ul>
But, .map() is not a problem. Your information data is an array, so you can .map() it with JSX.
Your problem is this:
<div key={info.date}>{info}</div>
info is an object, and React doesn't know how to print an object "beautifully". You want to use the string data (for example, {info.type}).
If you need to dump the full object, use JSON.stringify like this:
<div key={info.date}>{JSON.stringify(info)}</div>
React does not render array/object into a list by default. you have to be explicit in your code.
your first attempt is 'close' to correct, however, you can notice, ingredients and information are both arrays of objects. it means that you have to generate a tag for each index.
I used JSON.stringify to generate a unique key since using an index for key is bad practice.
The code below shows you one of the correct approaches using react.
const data = [
{
name: "Crock Pot Roast",
imageURL: "https://via.placeholder.com/150",
information: [
{
date: "24 July 2019",
type: "Main dish",
difficulty: "Easy",
time: "~50"
}
],
ingredients: [
{
quantity: "1",
name: " beef roast",
type: "Meat"
}
]
}
];
const CardRecipe = ({data}) => {
const { name, imageURL, information, ingredients } = data;
return (
<div key={name}>
<span>{name}</span>
<div>
<div>
<img src={imageURL} alt={name} />
<div>
{information &&
information.map(info => {
const { date, type, difficulty, time } = info;
return (
<ul key={JSON.stringify(info)}>
<li> {date} </li>
<li> {type} </li>
<li> {difficulty} </li>
<li> {time} </li>
</ul>
);
})}
</div>
<div>
{ingredients &&
ingredients.map(ingredient => {
const { quantity, name, type } = ingredient;
return (
<ul key={JSON.stringify(ingredient)}>
<li> {quantity} </li>
<li> {name} </li>
<li> {type} </li>
</ul>
);
})}
</div>
</div>
</div>
<div>food ingredients</div>
<div>food recipe</div>
</div>
);
};
export default function App() {
const first_reciepe = data[0];
return (
<div className="App">
<CardRecipe data={first_reciepe} />
</div>
);
}
Alternatively, you can print any lists by mapping once on the array, and then on each object/key to generate (x) amount of <"li"> tags.
Example:
const DoList = ({ data }) => {
return data.map(list => {
return (
<ul key={list}>
{Object.keys(list).map(key => {
const value = list[key];
return <li key={key}> {value} </li>;
})}
</ul>
);
});
};
Then you would use it as many time as you want like this:
<DoList data={ingredients} />
<DoList data={information} />
<DoList data={...} />

Render data from local JSON (with dynamic data) in ReactJs

I have a json array of objects as shown below, the number of objects can vary as well as the content of the "units" array inside the respective object can vary.
[{
"title": "title1",
"units": [{
"title": "unittitle1",
"status": "on",
"utilization": "76.4"
},
{
"title": "unittitle1",
"status": "on",
"utilization": "76.4"
},
{
"title": "unittitle1",
"status": "on",
"utilization": "76.4"
},
{
"title": "unittitle1",
"status": "on",
"utilization": "76.4"
}
]
},
{
"title": "title2",
"units": [{
"title": "unittitle1",
"status": "on"
},
{
"title": "unittitle1",
"status": "on"
}
]
}
]
I am calling the above json like this :
import React, { Component } from 'react';
import hardwareDetailsData from '../../../assets/json/UnitHardwareDetails.json';
import meter from '../../../assets/images/meter.svg';
class HardwareUnits extends Component {
constructor() {
super();
this.state = {
hardwareDetailsData: []
};
}
render() {
return hardwareDetailsData.map(hardwareData => (
<div className="f-14">
<ul className="list-inline list-pipe">
<li className="list-inline-item">
<span className="f-b">{hardwareData.units.title}</span>
<span className="status-text ml-2 text-success">{hardwareData.units.status}</span>
</li>
<li className="list-inline-item">
<span className="text-warning f-b">{hardwareData.units.utilization}</span>
<img src={meter} alt="Meter" className="ml-3 icon-meter-size" />
</li>
</ul>
</div>
));
}
}
export default HardwareUnits;
I am aware that the above code would require unique key for child elements. But, is there a optimal way to achieve the main objective i.e. to render the dynamic content of JSON in the list
I understood you want map in map like so:
return hardwareDetailsData.map(({ title, units }) => (
<div className="title">
{title}
units.map({ title, status, utilization}) => (
<div className="status">{status}</div>
));
</div>
));
Keep in mind shadowing, key (as you mentioned) and conditional rendering for utilization if it's the case. Also it could be splitted to components.
Your render method should be like this:
render() {
return hardwareDetailsData.map((hardwareItem, index) => (
<div className="f-14" key={index}>
<ul className="list-inline list-pipe">
{
hardwareItem.units.map((unitItem, index) => (
<li className="list-inline-item" key={index}>
<span className="f-b">{unitItem.title}</span>
<span className="status-text ml-2 text success">{unitItem.status}</span>
<span className="text-warning f-b">{unitItem.utilization}</span>
<img src={meter} alt="Meter" className="ml-3 icon-meter-size" />
</li>
))
}
</ul>
</div>
));
}

Categories

Resources