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
Related
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 ..
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"]);
I am learning React and right now I am trying to get my app to display a random joke (both question and answer) from the JSON file I created. However, I get the following error: getRandomJoke is not defined. I figured since it's a function, getRandomJoke does not need to get defined. Can someone explain where my mistake is and why I am getting this error?
This is the local JSON file I created:
SportsJokesData.js
const SportsJokesData = [
{
id: "1",
question: "What did the baseball glove say to the ball?",
answer: "Catch ya later!"
},
{
id: "2",
question: "What are the rules for zebra baseball?",
answer: "Three stripes and you’re out."
},
{
id: "3",
question: "Why are umpires always overweight?",
answer: "It’s their job to clean their plates."
}
export default SportsJokesData;
This is the component I created to get it displayed on my browser, SportsJokesApi.JS:
import React from 'react'
import SportsJokesData from './SportsJokesData';
class SportsJokesApi extends React.Component {
getRandomJoke(){
return SportsJokesData[(SportsJokesData.length * Math.random()) << 0]
}
render() {
return (
<p>{getRandomJoke}</p>
)
}
}
export default SportsJokesApi;
Do <p>{this.getRandomJoke()}</p> inside your render()
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 have tried many tutorials and so far, I can display items and get a little bit around React.
The URL structure is
/works/2
The query string of 2 is stored inside of pageID
I then fire the ajax call and filter the database to only show that data with .find()
This is the WorksPage.js file which will list the company work portfolio items.
import React, { Component } from 'react';
import axios from 'axios';
import './index.css';
class WorksPage extends Component {
constructor(props) {
super(props);
this.state = {itemList: []};
}
componentWillMount(){
const pageID = this.props.match.params.page;
axios.get('/api/works.json').then(function(response){
const result = response.data.find(i => i.id === pageID);
this.setState({ isLoaded: true, itemList: result });
}.bind(this));
}
componentDidMount() {
window.scrollTo(0, 0);
}
render(){
return(
<div className="workListing pageWrapper">
<section className="workListing-process">
<div className="smcontainer center txtc">
{this.state.itemList}
App Type: {this.state.itemList.sub}
App cars: {this.state.itemList.cars.map((car, i) =>
<div key={i}>
{car}
</div>
)}
</div>
</section>
</div>
)
}
}
export default WorksPage;
My JSON of works.json is
[{
"id": 0,
"img": "/images/slider.jpg",
"header": "GPS Module",
"sub": "iOS",
"link": "/works/0",
"cars":[ "Ford", "BMW", "Fiat" ]
}{
"id": 1,
"img": "/images/map-slider.jpg",
"header": "GPS Mapping Vectors",
"sub": "iOS",
"link": "/works/1",
"cars":[ ]
},{
"id": 2,
"img": "/images/slider.jpg",
"header": "GPS Module",
"sub": "Android",
"link": "/works/2",
"cars":[ "Ferrari", "BMW", "Land Rover" ]
}]
So far the {this.state.itemList} returns blank. The car list loop is also not working. Console.log will return the data of result if I do the console.log after the this.setState
First of all, do not use componentWillMount, it's both deprecated and not meant for calling APIs. Use componentDidMount instead.
I assume the problem is that pageID is string and id is number, thus nothing gets matched. Try converting pageID to number before comparing it.
const pageID = parseInt(this.props.match.params.page, 10);
You use 'this' keyword in callback function, and it refers to the call back itself not the component.
and also use componentDidmount, componenwillmount is not used anymore.
see here : componentWillMount
use this :
componentDidMount(){
let that = this;
const pageID = this.props.match.params.page;
axios.get('/api/works.json').then(function(response){
const result = response.data.find(i => i.id === pageID);
that.setState({ isLoaded: true, itemList: result });
};
}