I'm pulling an object out of an API that I'm using to show data on a website.
Here's the object:
{
"id": "25ce5d91-a262-4dcf-bb87-42b87546bcfa",
"title": "Les Houches The Hidden Gem Of The Chamonix",
"channel": "Cornelia Blair",
"image": "https://i.imgur.com/yFS8EBr.jpg",
"description": "Les Houches, located 6 kilometres from Chamonix, is a ski resort with a domain which extends from an altitude of 950 metres up to 1900 metres. Long descents through tree-lined slopes are combined with impressive views of the Mont Blanc massif and the Chamonix valley. Les Houches is twinned with the Russian villages of Sochi and Krasnaya-Polyana and was chosen by the International Olympic Committee to assist in the organization of the 2014 Winter Olympic Games. Watch for more fun facts!",
"views": "16,950",
"likes": "3,856",
"duration": "4:13",
"video": "https://project-2-api.herokuapp.com/stream",
"timestamp": 1622991672000,
"comments": [
{
"id": "7ba106bf-e74a-4c21-b59e-c485a30eea45",
"name": "Giovana Alpine",
"comment": "Wow! You can bet that we’ll be checking this place out when we’re in the area. The views look absolutely breathtaking. Thank you so much for sharing this with the world!",
"likes": 0,
"timestamp": 1623104591000
},
{
"id": "921f0e8d-f9d1-44db-b4a2-a2718339891e",
"name": "Victoire Lesage",
"comment": "Skiing is a lifestyle! This may be hard to believe now, but I once competed here for the World Cup. The Alps are at their most beautiful when you’re shredding down them FAST.",
"likes": 0,
"timestamp": 1623071160000
},
{
"id": "f7b9027b-e407-45fa-98f3-7d8a308ddf7c",
"name": "Sharon Tillson",
"comment": "Amazing footage of an amazing spot! It’s so inspiring to watch the sun rising over a landscape like this. I can only imagine how fresh the air must feel there on a snowy morning.",
"likes": 3,
"timestamp": 1623002522000
}
]
}
I'm then using this data and pass it down to others components, i.e. FeaturedVideo, FeaturedVideoDescription as it can be seen in the app.js, which is working just fine.
function VideoPage() {
const [details, setDetails] = useState([]);
const { videoId } = useParams();
useEffect(() => {
axios
.get(
`https://project-2-api.herokuapp.com/videos/${videoId}?api_key=removed for privacy`
)
.then((resp) => setDetails(resp.data));
}, []);
return (
<>
<FeaturedVideo selectedVideo={details} />
<FeaturedVideoDescription selectedVideo={details} />
<div className="information">
<div>
<CommentList selectedVideo={details} />
</div>
</div>
</>
);
}
However, when I then pass it down to the component: CommentList component it stops working. It's giving me the following error "cannot read properties of undefined (reading 'map')". Meaning, there seems to be an issue inside the CommentList component where I'm mapping through the data.
function CommentList({ selectedVideo }) {
return (
<>
<section className="comments">
{selectedVideo.comments.map((comment) => {
return (
<Comment
name={comment.name}
timestamp={comment.timestamp}
comment={comment.comment}
key={uniqid()}
/>
);
})}
</section>
</>
);
}
As it's undefined it means that it's not pulling the data from the API properly? But why is that?
I've also imported axios, useEffect and all the other things that are needed to run this code but decided to not include it in the code snippet here.
{selectedVideo?.comments?.map((comment) => {
You got an error because before to get your data and call the setState function, it is actually undefined so you must wait until it is available and the question mark will help you.
You can also check is details is an empty array or not and decide to render your component or not like this:
if(details.length > 0) return <CommentList selectedVideo={details} />
By the first render selectedVideo.comments is still undefined i.e., during initial render the Comment has comment prop with []
a small e.g.
const data = [];
console.log(data.some_prop.map(()=>{}))
so is the error ... you can use optional chaining one of few ways 😛
const data = [];
console.log(data?.some_prop?.map(()=>{}))
as selectedVideo?.comments?.map()... - no error here because it returns undefined as soon as the property doesn't exist as mentioned in docs link shared above.
after the first render, the component function gets called with new data, in which you have the data set earlier using setDetails ..
Related
I'm making an news app following a tutorial I'm fetching data from newapi my code looks same as tutorial but my component does not change after I update the state (this.state.articles) I'm using setState function i tried console logging the state it looks fine after the state is updated render methods runs but it does not change anything what could be worng
my code/component
import React, { Component } from 'react'
import NewsItem from './NewsItem'
export default class News extends Component {
articles = [
{
"source": {
"id": "espn-cric-info",
"name": "ESPN Cric Info"
},
"author": null,
"title": "PCB hands Umar Akmal three-year ban from all cricket | ESPNcricinfo.com",
"description": "Penalty after the batsman pleaded guilty to not reporting corrupt approaches | ESPNcricinfo.com",
"url": "http://www.espncricinfo.com/story/_/id/29103103/pcb-hands-umar-akmal-three-year-ban-all-cricket",
"urlToImage": "https://a4.espncdn.com/combiner/i?img=%2Fi%2Fcricket%2Fcricinfo%2F1099495_800x450.jpg",
"publishedAt": "2020-04-27T11:41:47Z",
"content": "Umar Akmal's troubled cricket career has hit its biggest roadblock yet, with the PCB handing him a ban from all representative cricket for three years after he pleaded guilty of failing to report det… [+1506 chars]"
},
{
"source": {
"id": "espn-cric-info",
"name": "ESPN Cric Info"
},
"author": null,
"title": "What we learned from watching the 1992 World Cup final in full again | ESPNcricinfo.com",
"description": "Wides, lbw calls, swing - plenty of things were different in white-ball cricket back then | ESPNcricinfo.com",
"url": "http://www.espncricinfo.com/story/_/id/28970907/learned-watching-1992-world-cup-final-full-again",
"urlToImage": "https://a4.espncdn.com/combiner/i?img=%2Fi%2Fcricket%2Fcricinfo%2F1219926_1296x729.jpg",
"publishedAt": "2020-03-30T15:26:05Z",
"content": "Last week, we at ESPNcricinfo did something we have been thinking of doing for eight years now: pretend-live ball-by-ball commentary for a classic cricket match. We knew the result, yes, but we tried… [+6823 chars]"
}
]
constructor() {
super();
this.state = {
articles: this.articles,
loading: false
}
}
async componentDidMount() {
const URL = "https://newsapi.org/v2/top-headlines?country=us&category=business&apiKey="
let data = await fetch(URL);
let parsedData = await data.json()
this.setState({
articles: parsedData.articles
})
console.log(this.state.articles)
}
render() {
console.log("render")
return (
<div>
<div className="container">
<h2 className='my-4 mx-4'> NewsMonkey - Top Headlines </h2>
<div className="row">
{this.articles.map((elem) => {
return <div className="col-md-4" key={elem.url}>
<NewsItem title={elem.title?elem.title.slice(42):""} desc={elem.description?elem.description.slice(0, 88): ""} url={elem.url} imgURL={elem.urlToImage} />
</div>
})}
</div>
</div>
</div>
)
}
}
this.articles and this.state.articles are not the same.
You have a static property this.articles that you are using in the render logic - this.articles.map(.... Your fetch is updating state (like it should be).
Update your render to read from this.state.articles and it should work.
Hi #Curious Your code is correct
you just need to pay attention when making the map
you are using this.articles which is a fixed (mock) list
you need to call map in this.state.articles because this is the state you change in didMount
I have a json array that contains some information :
[
{
"id": 1,
"imageUrl": "./img1.png",
},
{
"id": 2,
"imageUrl": "./img2.png",
}
]
I tried to make a class in which the elements were sorted and added the necessary link to the src, but the information from JSON is not put into JSX.
class Paintings extends Component {
render(){
return(
<ul>
{
paintings.map((s) => {
return (
<div>
<img src={s.imageUrl} alt={s.id} />
</div>
)
})
}
</ul>
)
The question is more general then just about images here. It is to import and loop a JSON object in JSX.
You have some syntax error in your code, I fixed them and shared an online editor implementation for you to have a look as well.
Save your json in a file and import that file in your react code then map the json object.
For the images location, although this is not the recommended method, I think you should put your images under public folder and make sure you have your json file with the correct path.
Sample JSON file I have structured, for your reference:
[
{
"id": 1,
"imageUrl": "image1.png"
},
{
"id": 2,
"imageUrl": "image2.png"
}
]
Then you should be able to use below code to be able to generate it.
import React, { Component } from "react";
import Painting from "./Painting";
class Paintings extends Component {
render() {
return (
<ul>
{Painting.map(({ id, imageUrl }) => (
<div key={id}>
<img src={imageUrl} alt={id} />
</div>
))}
</ul>
);
}
}
export default Paintings;
Here is a basic implementation for you to check out, we are able to map and see the items. I have added some sample images and please pay attention to json file and how its ready to map the JSX image objects based on the file paths.
https://codesandbox.io/s/react-json-into-jsx-19gj1w
I am a newbie to react. I have an object. I am trying to get specific value in an object but I am unable to do so. In my case, I am finding the value "Miami, USA" in the description of startLocation. The data object is retrieved from the server through api request and redux action.
1)Uncaught Error: Objects are not valid as a React child (found: object with keys { description, address}). If you meant to render a collection of children, use an array instead.
2)Unhandled Rejection (TypeError): Cannot read property 'description' of undefined
The data I got back from server looks like this,
{
"startLocation": {
"description": "Miami, USA",
"address": "301 Biscayne Blvd, Miami, FL 33132, USA"
},
"name": "Running",
"description": "something", // not this one
}
import React, { Component } from 'react';
import './Page.css';
class Page extends Component {
// some code
render() {
const eventData = this.props.events.data;
const {
name,
startLocation,
description,
} = eventData;
console.log(startLocation["description"]) // undefined
return (
<div className="container">
<div className="heading-group">
<h1 className="header">
<span>{name}</span>
</h1>
<span className="header-text">
{startLocation["description"]}>
</span>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
events: state.eventContainer,
});
export default connect(
mapStateToProps,
)(Page);
Any idea?
It works fine with the sample data provided. Perhaps your eventData hasn't imported properly.
const eventOne = {
"startLocation": {
"description": "Miami, USA",
"address": "301 Biscayne Blvd, Miami, FL 33132, USA"
},
"name": "Running",
"description": "something",
}
// destructure object javascript here
const {
name,
startLocation,
description,
} = eventOne;
console.log(description);
console.log(startLocation);
console.log(startLocation["description"]);
Coming with an Angular background to React, I can't wrap my head around on how to manipulate and restructure data before rendering it.
Let's say I pass this object from parent component to child:
{
"_id": "5c716c53591610007f6d44ef",
"model": {
"_id": "5c7166eb591610007f6d44d4",
"name": "E 300"
},
"serie": {
"_id": "5c716ba0591610007f6d44e2",
"name": "E-Class"
}
},
{
"_id": "5c716c60591610007f6d44f2",
"model": {
"_id": "5c7166f2591610007f6d44d6",
"name": "E 220"
},
"serie": null
},
{
"_id": "5c716c6a591610007f6d44f3",
"model": {
"_id": "5c7166fe591610007f6d44d8",
"name": "C 180"
},
"serie": {
"_id": "5c716ba4591610007f6d44e3",
"name": "C-Class"
}
},
{
"_id": "5c716c6e591610007f6d44f4",
"model": {
"_id": "5c716702591610007f6d44d9",
"name": "C 200"
},
"serie": {
"_id": "5c716ba4591610007f6d44e3",
"name": "C-Class"
}
},
{
"_id": "5c716c74591610007f6d44f5",
"model": {
"_id": "5c716705591610007f6d44da",
"name": "C 220"
},
"serie": {
"_id": "5c716ba4591610007f6d44e3",
"name": "C-Class"
}
}
I want to categorise each model name under their series.
For example C-Class: ['E300', 'E220'] etc, and also put models with no series defined into an object NoClass.
It's too much for an inline jsx (at least it seems so ??) so I need some helper functions, but I can't manage to do the manipulation because every time I get error that the data is undefined which means it tries to render code before it even appears there and gets modified.
So pretty much I want to filter data into new objects and then render these instead of the original props data. And I don't know how to do that all before rendering
My current attempt at doing it inline - it does render however it doesn't check for empty series objects and breaks when where are more than 1 different serie:
class ModelSelect extends React.Component {
render() {
const { models } = this.props
return (
models && <div className={"ModelSelect_Wrapper"}>
<Select placeholder={"Model"} loading={models.loading} disabled={models.data.length === 0}>
{_.uniqBy(models.data, 'name').map((serie) =>
<OptGroup label={serie.serie.name}>
{!models.loading && models.data.map((model, index) =>
<Option value={model.model._id} key={index}>{model.model.name}</Option>
)}
</OptGroup>
)}
</Select>
</div>
);
}
The easiest way to do this is using stores. You have several options available, such as Redux, Flux, and Mobx. Unfortunately, learning to use Stores is a bit hard. coming from angular, i would compare a store to $scope from angular.js 1x, with the difference being that there is no magic causing the view to rerender, you have to rely on react detecting a change in the props in order for rerender to occur. the idea is that the store is a data structure, and when it is modified, the components which are using the store as a prop will be rerendered. this works great especially if you are fetching data async.
the basic pattern would be
use componentDidMount() to load the data in Higher Order Component
transform the data and update the store with the new data
you are passing the store to your component like <MyComponent store={store} />
when data is loaded, you pass the store to the child component
doing it this way your component may look something like
class MyComponent extends React.Component {
static defaultProps = {
store:{}
}
state = {
loading: true
}
componentDidMount() {
this.props.store.loadSomeData().then(()=>this.setState({loading:false}));
}
render(){
return this.state.loading ? null : <MyChildComponent store={store} />
}
}
please remember, you are responsible for loading and transforming the data in your store, and this is just a generalized overview of how your component show load and receive that transformed data. your exact approach will vary wildly based upon the store you are using.
My answer can't be any more specific than this, because there is far too much to cover, and the scope of the work required varies highly depending on the library you use. For example, mobx-3x all you do is decorate your component with #inject and #observer and it handles the shouldComponentUpdate() implementation for you.
So your, first step is to pick a store, and then go from there. best of luck.
I currently have jsx in my React component that will return certain features based on if there is a certain value in an object. For example in my React component:
if(customer.product.id === "543"){
return (
<div className="text-align-left">
<div className="row">
<div className="col-xs-12 font-weight-bold">
<p>Some text here</p>
</div>
</div>
</div>
);
} else {
return (
<div className="text-align-left">
<div className="row">
<p>Different text here</p>
</div>
</div>
</div>
);
'customer' is coming from my Redux state and dispatching appropriately, and is as follows:
{
"product": {
"id": "543",
},
"info": [
{
"name": {
"title": "Mr",
"first_name": "John",
"last_name": "Smith",
},
"dob": "1956-06-06",
"phone": [
{
"number": "5555555555",
"ext": "234",
}
]
}
]
}
I know it is dispatching correctly because if I try to access customer.info[0].name.last_name in the jsx, it returns the correct value.
Yet, when I try to get the value for customer.product.id I am met with the error
TypeError: Cannot read property 'id' of undefined
From reading other questions here, it seems the issue may be that the object I'm getting from the Redux state is asynchronous, but if it is able to go in and access customer.info[0].name.last_name, why can't it do the same with customer.product.id? I tried using a try catch block, but that didn't work out either (also, having an if else statement in a try catch isn't exactly dry code). Much of the other questions I found on here with this issue when using React were dealing with a component with a local state an.
I apologize if this question is a duplicate of another question, but I can't seem to find a question that fits this situation. Thank you!