How to fetch and render object of objects in next js? - javascript

I have following code and I am trying to display the data from the object of objects. The error that I receive is TypeError: Cannot read properties of undefined. I understand that the data isn't in the format of 'array of objects', however I still don't know how to properly map it. I would appreciate some help here...
import Layout, { siteTitle } from '../components/layout';
import { useState, useEffect } from 'react'
export default function Home({ allPosts }) {
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<section>
{Object.values(allPosts.bpi).map(({ section, idx }) => (
<li className={utilStyles.listItem} key={idx}>
{section.description}
<br />
{section.code}
<br />
</li>
))}
</section>
</Layout>
);
}
export async function getStaticProps() {
let data = [];
let error = "";
try {
const res = await fetch(
"https://api.coindesk.com/v1/bpi/currentprice.json",
);
data = await res.json();
} catch (e) {
error = e.toString();
}
return {
props: {
allPosts: data,
error,
},
};
}
Logged data: enter image description here
Object looks like this
{
"chartName": "Bitcoin",
"bpi": {
"USD": {
"code": "USD",
"symbol": "$",
"rate": "20,220.5728",
"description": "United States Dollar",
"rate_float": 20220.5728
},
"GBP": {
"code": "GBP",
"symbol": "£",
"rate": "16,896.1488",
"description": "British Pound Sterling",
"rate_float": 16896.1488
},
}
}

You don't access the USD and GBP objects. You are just getting their names. But you can access them by name like this:
<section>
{Object.values(allPosts.bpi).map((section, idx) => (
<li className={utilStyles.listItem} key={idx}>
{allPosts.bpi[section].description}
<br />
{allPosts.bpi[section].code}
<br />
</li>
))}
</section>
EDIT
It should be section.description instead of allPosts.bpi[section].description. Same for the object code.
<section>
{Object.values(allPosts.bpi).map((section, idx) => (
<li className={utilStyles.listItem} key={idx}>
{section.description}
<br />
{section.code}
<br />
</li>
))}
</section>

You are using the map method wrong.
Try this.
Object.values(allPosts.bpi).map((section, idx) => ....

Related

React - Cards are not rendering as I am calling for the data in this API

I am receiving a response from the API, but the data doesn't display on the card. I don't think it has much to do with the data I think it has much to do with the card appearing itself first. Here is how the search file is set up, pretty straight forward. As you see I did set up a container to hold the card as I map through it.
import '../styles/searchPage.css'
import SearchCard from '../components/SearchCard';
const API_URL = 'https://api.openbrewerydb.org/breweries?by_city';
const brewery1 = {
"id": "10-barrel-brewing-co-san-diego",
"name": "Nirmanz Food Boutique",
"brewery_type": "large",
"street": "1501 E St",
"phone": "7739888990 ",
"address": null,
"city": "San Diego",
"state": "California",
"postal_code": "92101-6618",
"country": "United States",
}
function SearchPage() {
const [cards, setCards] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const searchRestaurants = async (name) => {
const req = await fetch(`${API_URL}&s=${name}`);
const data = await req.json()
console.log(data[0].name)
setCards({data: data.name})
}
useEffect(() => {
searchRestaurants('')
}, [])
return (
<div className='search'>
<h1>Enter a City or Town name</h1>
<div className='search-container'>
<input
type="text"
name="search"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyPress={(e) => {
if (e.key === 'Enter'){
setCards(searchTerm);
}
}}
placeholder="Search..."
class="search-input"
/>
<button
className='next'
onClick={()=> searchRestaurants(searchTerm)}
>Go</button>
</div>
{
cards?.length > 0
? (
<div className="container">
{cards.map((card) =>(
<SearchCard brewery1={brewery1}/>
))}
</div>
) :
(
<div className="empty">
<h2>Found 0 Breweries</h2>
</div>
)
}
</div>
);
}
export default SearchPage
Here is the my JSX for my search card labeling out what I want to display inside that card.
import '../styles/searchPage.css'
const SearchCard = ({brewery1}) => {
return (
<div className="card">
{/* <img src={brewery1.Poster !== 'N/A' ? brewery1.Poster : 'https://via.placeholder.com/400'} alt={brewery1.name /> */}
<div>
<span>{brewery1.id}</span>
<h3>{brewery1.brewery_type}</h3>
<h2>{brewery1.street}</h2>
<h2>{brewery1.adress}</h2>
<h2>{brewery1.phone}</h2>
<h2>{brewery1.city}</h2>
<h2>{brewery1.state}</h2>
<h2>{brewery1.postal_code}</h2>
<h2>{brewery1.country}</h2>
</div>
</div>
)
}
export default SearchCard;
Change this:
setCards(data);
And this:
<SearchCard brewery1={card}/>
In your searchRestaurant method set the data like this:
const searchRestaurants = async (name) => {
const req = await fetch(`${API_URL}&s=${name}`);
const data = await req.json()
//make sure that data got your restaurants list
setCards(data)
}
and in the render like this:
cards.map(card => <SearchCard brewery1={card}/>)

Render JSON data (from reddit API) with reactjs

Very new to React, so I might be approaching this the wrong way... I want my app to take input from a text input field, retrieve a JSON from the reddit API (the url is built from the text input), and then render data from the JSON, looping through each of the entries. I'm using useState to trigger the data render. I can successfully retrieve the data and output specific values, but I want to be able to have a loop that dynamically outputs the data into various HTML elements.
Here's what I have so far that allows me to output some specific values as an example:
import React, { useState } from 'react';
const App = () => {
const [retrievedData, setRetrievedData] = useState([])
const runSearch = async() => {
const searchInput = document.getElementById('searchInput').value
const searchUrl = 'https://www.reddit.com/r/' + searchInput + '/new/.json?limit=5'
const response = await fetch(searchUrl)
const redditResponse = await response.json()
setRetrievedData(<>
<p>{JSON.stringify(redditResponse.data.children[0].data.author)}</p>
<p>{JSON.stringify(redditResponse.data.children[0].data.title)}</p>
</>)
}
return (
<>
<section>
<input type="text" id='searchInput' placeholder='Enter a subreddit...'></input>
<button onClick={runSearch}>
Get Data
</button>
<div>{retrievedData}</div>
</section>
</>
);
};
export default App;
And here's an example of the JSON that is retrieved from the reddit API, just stripped down with only the example values I use in my code above:
{
"kind": "Listing",
"data": {
"modhash": "",
"dist": 5,
"children": [
{
"kind": "t3",
"data": {
"author": "author1",
"title": "title1"
}
},
{
"kind": "t3",
"data": {
"author": "author2",
"title": "title2"
}
},
{
"kind": "t3",
"data": {
"author": "author3",
"title": "title3"
}
},
{
"kind": "t3",
"data": {
"author": "author4",
"title": "title4"
}
},
{
"kind": "t3",
"data": {
"author": "author5",
"title": "title5"
}
}
],
"after": "t3_jnu0ik",
"before": null
}
}
I just need the final rendered output to be something like:
<h2>TITLE 1</h2>
<h4>AUTHOR 1</h4>
<p>SELFTEXT 1</p>
...and repeated for each post data that is retrieved.
I've seen a variety of different ways to render JSON data and many of them show either loops and/or the .map() method, but I can't ever seem to get those to work, and wonder if it's an issue with the useState. Perhaps there is some way I should be rendering the data some other way?
You don't need set jsx to state, you can directly iterate children data with map
Try this
const App = () => {
const [retrievedData, setRetrievedData] = useState([])
const runSearch = async() => {
const searchInput = document.getElementById('searchInput').value
const searchUrl = 'https://www.reddit.com/r/' + searchInput + '/new/.json?limit=5'
const response = await fetch(searchUrl)
const redditResponse = await response.json()
if (redditResponse.data.children && redditResponse.data.children.length) {
setRetrievedData(redditResponse.data.children)
}
}
return (
<>
<section>
<input type="text" id='searchInput' placeholder='Enter a subreddit...'></input>
<button onClick={runSearch}>
Get Data
</button>
<div>
{
retrievedData.map((children, index) => {
return (
<div key={children.data.author + index}>
<div>Kind: { children.kind }</div>
<div>Author: { children.data.author }</div>
<div>Title: { children.data.title }</div>
</div>
)
})
}
</div>
</section>
</>
);
};

How to fetch inner API data in ReactJS?

What I am trying to do is fetch the inner data of blog_set. But in my case, I'm getting a null value (usually nothing is output).
Is this the correct way to get the value: {bloglist.blog_set.title} ?
api-data:
[
{
"url": "http://localhost:8000/api/category/brown",
"id": 1,
"title": "brown",
"slug": "brown",
"image": "http://localhost:8000/media/category/bg_1.jpg",
"description": "",
"created_on": "2020-05-08T15:21:02Z",
"status": true,
"blog_set": [
{
"id": 6,
"url": "http://localhost:8000/api/blog_detail/test3",
"title": "test3",
"slug": "test3",
"image": "http://localhost:8000/media/blog/author.jpg",
"description": "test3",
"created_on": "2020-05-13T13:36:45Z",
"status": true,
"category": [
1
]
}
]
}
]
./src/Category.js
export default class App extends Component{
state = {
bloglist: [],
};
componentDidMount() {
this.fetchData();
}
fetchData = async () => {
try {
const response = await fetch("http://localhost:8000/api/category");
const jsonResponse = await response.json();
this.setState({ bloglist: jsonResponse });
} catch (error) {
console.log(error);
}
};
render(){
{
const { bloglist } = this.state;
return(
<div>
{bloglist.map((bloglist) => (
<div>
<h3 class="mb-2">{bloglist.blog_set.title}</h3>
</div>
))}
</div>
);
}
}
}
blog_set is an array so it does not have an attribute/memeber/item called title. You should define at what index you want the data.
bloglist.blog_set[0].title
Or iterate over blog_set too
As bloglist is also an array you need to use one more .map() or as bloglist[0].blog_set[0].title
Example:
{bloglist.map((bloglist) => (
<div>
<h3 class="mb-2">{bloglist.blog_set.map(i=> i.title)}
</h3>
</div>
))}
blogList.map() will iterate the parent Array of objects to get blog_set and blog_set.map() will now iterate the blog_set to get list title
{bloglist.map((bloglist) =>(
<div>
<h3 class="mb-2">{bloglist.blog_set.map((list)=>( list.title)}</h3>
</div>)}
blog_set is an array. In order to iterate it, use map and {title}. In each iteration of your blog_set object, there is a key named title (destructured object).
<div>
{bloglist.map((bloglist) => (
<div>
<h3 class="mb-2">{blog_set.map(({title})=>title))}</h3>
</div>
))}
</div>

is there a way to query data based on a particular item from a json object?

I have user interface which looks should look like this
and that picture above is just pure HTML.
So when I tried to create it with React, I am failing to align the tv shows with a particular TV channel overflowing horizontally as per the channel.
Picture of what I get in React
I am querying the data from json files that have the objects and the TV channel object looks like
{
"groupID": 16481,
"hasMediathek": true,
"storeUrlAndroid": null,
"storeUrlApple": null,
"liveWeb": "https://www.zdf.de/live-tv",
"liveApp": null,
"defaultOrder": 1000,
"hdp": false,
"quality": 2,
"name": "ZDFneo HD",
"isEncrypted": false,
"isHD": false,
"dvbTriplet": "dvb://0.0.0",
"id": null,
"major": true
}
this is connected to the shows through its groupID which shows up as channelID in the shows Object. Below is a sample for the shows object
{
"_id": "5b1f5c7da6cdf0cbbdb7e700",
"showID": 892149863,
"channelID": 16481,
"title": "Masters of Sex",
"subtitle": "Auf frischer Tat ertappt (Dirty Jobs)",
"serie": {
"no": "4",
"title": "Auf frischer Tat ertappt",
"seasonno": "2",
"info": "Staffel 2 | Folge 4"
}
this what I have done to query the data for channels
import stations from "../data/channels.json";
import data1 from "../data/1.json";
import data2 from "../data/2.json";
import data3 from "../data/3.json";
import data4 from "../data/4.json";
import data5 from "../data/5.json";
import data6 from "../data/6.json";
class Contents extends React.Component {
constructor(){
super();
this.trans = this.trans.bind(this);
}
station = { ...stations };
shows = { ...data1, ...data2, ...data3, ...data4, ...data5, ...data6 };
trans(){
Object.values(station.result.channels).map(value => {
console.log(value["groupID"], "odgdggddgdd");
return value["groupID"];
});
}
render() {
return (
<Fragment>
<TopNavBar />
<BottomNavBar />
<div className="row">
<section className="left-menus">
<div className="left-items">
{Object.values(station.result.channels).map(value => (
<div>
<img
src={`https://cdn.hd-plus.de/senderlogos/bright-cropped/${value["groupID"]}.png`}
alt=""
/>
</div>
))}
</div>
</section>
<section className="item-center">
{
Object.values(shows.result).map(value => (
<div className="shows">{
<div className="grid-items">
<div className="item">
<small>{value.startime}</small>
<small>value.resolution</small>
</div>
<div className="item-name">{value.title}</div>
</div>
}
</div>))}
</section>
</div>
</Fragment>
);
}
}
export default Contents;
I need some help with aligning the channels with their respective stations. I hope this is descriptive enough. Thank you
Updated Code for the tiles
<section className="item-center">
{
Object.values(station.result.channels).map(value => (
<div className="shows">{
shows.result.find(show => show['channelID'] === value['groupID']).map(item => (
<div className="grid-items">
<div className="item">
<small>{item.startime}</small>
<small>value.resolution</small>
</div>
<div className="item-name">{item.title}</div>
</div>
))}
</div>))}
</section>
error message
when I try to add Object.values() around it I get this
The correct solution for this (as found in the comments) is to use the filter() function. a find() function would only give back one object, or undefined, so you cannot use map on it.
shows.result.filter(show => show['channelID'] === value['groupID']).map(item =>
())
This will return every object where the channelID equals the groupID, which you can then map to a ui element.
https://www.w3schools.com/jsref/jsref_filter.asp

Props data are not rendering

I have a problem rendering my props data
Here I'm trying to pass props to a component with mapped data from a sample data set
const weatherComponents = weatherData.items.map(weather => {
return(
<div key={weather.forecasts.area}>
<WeatherForecast
name={weather.forecasts.area}
condition={weather.forecasts.forecast}>
</WeatherForecast>
</div>
)})
return(
{weatherComponents} )
This is the component
function WeatherForecast(props) {
return(
<div>
<p>Name: {props.name}</p>
<p>Condition: {props.condition}</p>
</div>
)}
This is the sample data set
{
"area_metadata": [
{
"name": "Yishun",
"label_location": {
"latitude": 1.418,
"longitude": 103.839
}
}
],"items": [
{
"forecasts": [
{
"area": "Yishun"
"forecast" : "cloudy"
}
]}
]}
In my browser, it shows Warning: Each child in a list should have a unique "key" prop. and the data are not rendering, it only appears "Name: " without the area name from the data set. Am I mapping the data in the wrong way? Help TT
You have 2 options ... well, 3.
You need an array in "area_metadata" and "items":
1.1. The fast solution:
const weatherComponents = weatherData.items.map(weather => {
return(
<div key={weather.forecasts[0].area}>
<WeatherForecast
name={weather.forecasts[0].area}
condition={weather.forecasts[0].forecast}>
</WeatherForecast>
</div>
)
})
return({weatherComponents})
1.2 The right solution:
const weatherComponents = weatherData.items.map(weather => {
return weather.forecasts.map( casts => (
<div key={casts.area}>
<WeatherForecast
name={casts.area}
condition={casts.forecast}>
</WeatherForecast>
</div>
))
})
return({weatherComponents})
2. You do not need an array:
{
"area_metadata": [
{
"name": "Yishun",
"label_location": {
"latitude": 1.418,
"longitude": 103.839
}
}
],
"items": [
{
"forecasts": {
"area": "Yishun"
"forecast" : "cloudy"
}
}
]
}
Just replace
const weatherComponents = weatherData.items.map(weather => {
return(
<div key={weather.forecasts.area}>
<WeatherForecast
name={weather.forecasts.area}
condition={weather.forecasts.forecast}>
</WeatherForecast>
</div>
)})
return(
{weatherComponents} )
with
const weatherComponents = weatherData.items.map(weather => {
const {area, forecast} = weather.forecasts[0];
return(
<div key={area}>
<WeatherForecast
name={area}
condition={forecast}>
</WeatherForecast>
</div>
)})
return(
{weatherComponents} )

Categories