React JS can't able to render components from Array - reactjs

When i try to render my component using map function it show error that this.state.articles is undefined but it work well when i use map function on componentDidMount() OR other functionsenter image description here
import React, { Component } from 'react'
import Items from './Items'
export default class News extends Component {
constructor() {
super()
this.state = {
articles: this.articles
}
}
async componentDidMount() {
let GET = 'https://newsapi.org/v2/top-headlines?country=us&apiKey=256fd7f5eed44e16b08f3e23cb2dabdb'
let Data = await fetch(GET)
let parse = await Data.json()
this.setState({ articles: parse.articles })
}
render() {
return (
<div className='container my-3'>
<h1>R News | Top Headlines</h1>
<div className='row'>
{
this.state.articles.map((ele)=>{
return <div></div>
})
}
</div>
</div>
)
}
}
k.imgur.com/ALxmx.png

I see that you're using this.articles in the constructor but I don't see it declared anywhere in your class. I think you meant to initialize your state with an empty array and you retrieve the articles from the backend:
constructor() {
super()
this.state = {
articles: []
}
}

The reason is because it takes time for the information to get back from newsapi.org so you can either use a async function or add a '?' which will be a check to see whether the article that came back from newsapi is back or not.
Try this render
return (
<div className='container my-3'>
<h1>R News | Top Headlines</h1>
<div className='row'>
{
this.state.articles?.map((ele, index)=>{
return <div key={index}></div>
})
}
</div>
</div>
)

This is because articles are undefined when the data is not yet loaded try this code,
import React, { Component } from 'react'
import Items from './Items'
export default class News extends Component {
constructor() {
super()
this.state = {
articles: this.articles
}
}
async componentDidMount() {
let GET = 'https://newsapi.org/v2/top-headlines?country=us&apiKey=256fd7f5eed44e16b08f3e23cb2dabdb'
let Data = await fetch(GET)
let parse = await Data.json()
this.setState({ articles: parse.articles })
}
render() {
return (
<div className='container my-3'>
<h1>R News | Top Headlines</h1>
<div className='row'>
{
this.state.articles && this.state.articles.map((ele)=>{
return <div></div>
})
}
</div>
</div>
)
}
}

Related

TypeError: Cannot read property 'map' of undefined When trying to map multiple arrays

I am trying to map multiple arrays at the same time and im not sure if this is how you do it. I am getting the error
TypeError: Cannot read property 'map' of undefined
When trying the following code
import React, { Component } from 'react';
import Axios from 'axios';
import NavBar from '../header-footer/nav-bar'
import Featured from './FeaturedMealplan'
import RecipeItem from './RecipeItem'
export default class MealPlanDetail extends Component {
constructor(props) {
super(props);
this.state = {
currentId: this.props.match.params.slug,
mealplanItem: {}, // Full mealplan
mealplanRecipes: [], // Contains recipe names and difficulty.
}
}
getMealplanItem() {
Axios.get(`http://localhost:5000/get-mealplan/${this.state.currentId}`
).then(response => {
console.log("response", response)
this.setState({
mealplanItem: response.data.mealplan,
mealplanRecipes: this.state.mealplanRecipes.concat(response.data.mealplan["recipes"]),
mealplanIngredients: this.state.mealplanIngredients.concat(response.data.mealplan["recipe_info"]),
recipeItem: response.data.mealplan.recipes
})
}).catch(error => {
console.log("mealplan-detail GET Error ", error)
})
}
componentDidMount() {
this.getMealplanItem();
}
render() {
const renderRecipe = this.state.recipes.map((recipe, idx) => {
return (
<div key={idx}>
<h1>{recipe.recipe_name}</h1>
<h2>Recipe Difficulty: <span>{recipe.recipe_dificulty}</span></h2>
<div>
<RecipeItem recipeItem={this.state.recipeItem} />
</div>
</div>
)
})
return (
<div>
<NavBar/>
<Featured/>
{renderRecipe}
</div>
)
}
}
Data that is given: https://pastebin.com/uYUuRY6U
I just need to be able to format it correctly which this is how I would like it formatted in the renderRecipe return. I am new to mapping and do not know if there is a way to fix or a better way.
Some issues in the code that we can improve on:
this.state.recipes seems to be undefined in your logic. Is it a typo?
I would suggest implementing renderRecipe as a function instead of a variable.
You would only hope to render renderRecipe when there is data, but when your component is being mounted, this.state.recipes is undefined. It would only have value when getMealplanItem gets a response and being defined in the callback. So you should check whether the value is defined before rendering.
Please refer to my comments in the code below.
import React, { Component } from "react";
import Axios from "axios";
import NavBar from "../header-footer/nav-bar";
import Featured from "./FeaturedMealplan";
import RecipeItem from "./RecipeItem";
export default class MealPlanDetail extends Component {
constructor(props) {
super(props);
this.state = {
// ... define `recipes` if that's what you want
};
}
getMealplanItem() {
Axios.get(`http://localhost:5000/get-mealplan/${this.state.currentId}`)
.then((response) => {
console.log("response", response);
// ... set state `recipes` here if that's what you want
this.setState({
mealplanItem: response.data.mealplan,
mealplanRecipes: this.state.mealplanRecipes.concat(
response.data.mealplan["recipes"]
),
mealplanIngredients: this.state.mealplanIngredients.concat(
response.data.mealplan["recipe_info"]
),
recipeItem: response.data.mealplan.recipes
});
})
.catch((error) => {
console.log("mealplan-detail GET Error ", error);
});
}
componentDidMount() {
this.getMealplanItem();
}
render() {
const renderRecipe = () => {
// change renderRecipe from a variable to a function
if (!this.state?.recipes) {
// check whether `recipes` is a defined value
return null;
}
return this.state.recipes.map((recipe, idx) => {
return (
<div key={idx}>
<h1>{recipe.recipe_name}</h1>
<h2>
Recipe Difficulty: <span>{recipe.recipe_dificulty}</span>
</h2>
<div>
<RecipeItem recipeItem={this.state.recipeItem} />
</div>
</div>
);
});
};
return (
<div>
<NavBar />
<Featured />
{renderRecipe()} // it's a function call now
</div>
);
}
}
There is never a this.state.recipes defined. Based on data type and comment
this.state = {
currentId: this.props.match.params.slug,
mealplanItem: {}, // Full mealplan
mealplanRecipes: [], // Contains recipe names and difficulty.
}
I will assume you meant for it to really be this.state.mealplanRecipes.
Your render then becomes
const renderRecipe = this.state.mealplanRecipes.map((recipe, idx) => {...
This can easily handle the initial render with an empty array.

Objects are not valid as a React child (found: object with keys {items}). If you meant to render a collection of children, use an array instead

I am trying to render multiple cards, each card for each news. But I have an error that is mentioned in the title. Something went wrong when React tries to render cards.
export default class NewsCard extends Component{
newsService = new NewsService()
state = {
newsList: null,
}
componentDidMount() {
this.newsService.getByCategory('Belarus').then((newsList) => {
console.log('News: ',newsList);
this.setState({
newsList
})
})
}
renderItems(arr: any){
return arr.map((article: any) => {
console.log(article)
return (
<div className="card col-sm-12 col-md-6 col-lg-3">
<img src={article.urlToImage} alt="icon" />
<div className="info">
<p><a href={article.url}>{article.title}</a></p>
<p>{article.description}</p>
</div>
</div>
)
})
}
render() {
const { newsList } = this.state
if(!newsList) {
return <Loader />
}
const items = this.renderItems(newsList)
return ({items})
}
}
Also here I am adding a piece of code from my NewsService which may help to figure out what happens.
async getByCategory(category: string) {
const news = await this.newsapi.v2.topHeadlines({
q: category,
sortBy: 'popularity'
})
return await news.articles
}
The problem is with this:
return ({items})
Your particular error is caused because you are creating an object by using curly braces.
What your code is really doing is this:
return { items: items }
That is an object and react doesn't know how to render an object.

I cannot get image to render after a network query

Using Parse, I am querying the database and getting an imageURL back. React is not updating the dom.
componentWillMount and just regular curly brackets.
export const profileImage = async objectId => {
const query = new Parse.Query(Parse.User);
query.equalTo("objectId", objectId);
const image = await query.find();
console.log(typeof image[0].attributes.image);
console.log(image[0].attributes.image);
return image[0].attributes.image; // return image;
}; // Query for a specific user and get image!
I imported it currently and it does the console logs so the function is executing but never rendering.
export default class MyDashboard extends Component {
constructor(props) {
super(props);
this.state = {
profilePic: "",
};
}
componentDidMount() {
this.setState({ profilePic: profileImage(window.localStorage.objectId) });
}
render() {
return (
<div>
<Sidebar />
<div className={style.Componentcontainer} />
<div className={style.main}>
</div>
<div className={style.profile}>
<div> {this.state.profilePic} </div>
}
I eventually plan to put the string into an image tag, I just got to get this rendering first.
Your function is asynchronous, so setState will not wait and will render undefined.
To fix this, you should return a promise, and consume it with a .then() and set the state there instead. You should also use window.localStorage.getItem(), rather than trying to access a property immediately.
export const profileImage = objectId => {
const query = new Parse.Query(Parse.User);
query.equalTo("objectId", objectId);
return query.find();
};
export default class MyDashboard extends Component {
constructor(props) {
super(props);
this.state = {
profilePic: ""
};
}
componentDidMount() {
profileImage(window.localStorage.getItem(objectId)).then(image => {
this.setState({ profilePic: image[0].attributes.image });
});
}
render() {
return (
<div>
<Sidebar />
<div className={style.Componentcontainer} />
<div className={style.main} />
<div className={style.profile}>
<img src={this.state.profilePic} />
</div>
</div>
);
}
}

I can not access the right data of property sent from parent to child component

I am facing an issue with react and I am totally stuck. I have 3 components: channel as a parent and header and story as a children:
class Channel extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
this.props.getChannels();
}
render() {
return (
<div>
<div className="col-xs-12 col-md-8 col-lg-8>
<div className="row">
<Header activeChannelList={this.props.channels.channelsArr}/>
</div>
<div className="row">
{
this.props.channels.channelsArr.map((item, i) => <StoryBoard
newsChanel={item}
key={"storyBoard" + i}
></StoryBoard>)
}
</div>
</div>
<div className="col-xs-12 col-md-2 col-lg-2 color2">.col-sm-4</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
channels: state.channelReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
getChannels: () => {
dispatch(getChannels());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Channel);
As you can see I have a ajax call with this.props.getChannels(); and I put it in componentDidMount to make sure that it is called before rendering then after I pass the channels to the Header ans story which are children components.
Now my problem is when I try to access it in Header via console.log(this.props.activeChannelList); I get 0 thought I should have 5 channels. More intrestingly when I try to access the props I send in Stroryboard I can easily access them without any problem. The following is my code for Header:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
console.log("dddddddddddddddddddddddddddddddddddddddddd");
console.log(this.props.activeChannelList);// I get 0 though I should get 5
}
render() {
return (
<div className="col-xs-12 header tjHeaderDummy">
<div className="col-xs-1"></div>
</div>
);
}
}
And my storyboard is :
class StoryBoard extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
if(this.props.isFreshLoad ){
do sth
}
}
render() {
return (
<div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
stories: state.storyBoardReducer
};
};
const mapDispatchToProps = (dispatch) => {
return {
//some funcs
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(StoryBoard);
Can anyone help?
U r printing the value in componentDidMount method in Header component, this lifecycle method get called only once, if ur api response come after the rendering of Header, it will never print 5, put the console in render method, so that at any time when u get the response it will populate the value.
From Docs:
componentDidMount: is invoked immediately after a component is mounted
first time. This is where AJAX requests and DOM or state updates
should occur.
Try this Header Comp, it will print the proper value:
export class Header extends React.Component {
constructor(props) {
super();
}
componentDidMount() {
}
render() {
return (
<div className="col-xs-12">
{this.props.activeChannelList}
</div>
);
}
}

Passing a function in props to a component

I'm new to react and trying to pass a global function to components to avoid repeating it in each of them. That doesn't work, I get an undefined error when I try to call it in the components.
Here is my code :
import React from 'react';
//components
import League from './League';
class App extends React.Component {
state = {
leagues: {},
};
componentDidMount() {
this.getLeagues();
}
get(url) {
var myHeaders = new Headers();
myHeaders.append("Accept", "application/json");
myHeaders.append("X-Mashape-Key", "mysecretkeyblablabla");
var myInit =
{
headers: myHeaders
};
return fetch(url,myInit)
.then(function(response) {
if(response.ok) {
return response.json().then(function(json) {
return json.data;
});
}
});
};
getLeagues() {
this.get('https://sportsop-soccer-sports-open-data-v1.p.mashape.com/v1/leagues').then((data) => {
this.setState({leagues: data.leagues});
});
}
render() {
const leagues = Object
.keys(this.state.leagues)
.map(key => <League get={this.get} key={key} details={this.state.leagues[key]} />
);
return(
<div className="App">
<div className="App-header">
<h1>Welcome to Foot Stats app (made in ReactJS)</h1>
</div>
<p className="App-intro">
Here is the place where I should put the countries.
</p>
<ul>
{leagues}
</ul>
</div>
);
};
}
export default App;
and my League component
import React from 'react';
import Season from './Season';
class League extends React.Component {
state = {
seasons: {},
};
constructor(props) {
super(props);
}
componentDidMount() {
//this.getSeasonsAvailable(this.props.details.league_slug);
}
getSeasonsAvailable(league) {
const url = 'https://sportsop-soccer-sports-open-data-v1.p.mashape.com/v1/leagues/{league_slug}/seasons'.replace('{league_slug}',league);
const seasons = [];
console.log(this.props);
this.props.get(url).then((data) => {
data.seasons.map(function(object, i) {
seasons[data.seasons[i].identifier] = data.seasons[i];
});
this.setState({seasons: seasons});
});
};
render() {
const seasons = Object
.keys(this.state.seasons)
.map(key => <Season key={key} league_slug={this.props.details.league_slug} details={this.state.seasons[key]} />
);
return (
<li>
<span onClick={this.getSeasonsAvailable.bind(this.props.details.league_slug)}>{this.props.details.nation} : {this.props.details.name}</span>
<ul>
{seasons}
</ul>
</li>
);
}
static propTypes = {
get: React.PropTypes.func.isRequired
};
}
export default League;
When I click on the season component, I get this error :
Cannot read property 'get' of undefined
And my console.log(this.props) returns me undefined.
Thanks !
You just need to change
<span onClick={this.getSeasonsAvailable.bind(this.props.details.league_slug)}>
to
<span onClick={this.getSeasonsAvailable.bind(this, this.props.details.league_slug)}>
Apart from this, if you want to use ES6 way to do this. You can use arrow functions
<span onClick={() => this.getSeasonsAvailable(this.props.details.league_slug)}>
or you can bind the function getSeasonsAvailable in the constructor using
constructor() {
super();
this.getSeasonsAvailable = this.getSeasonsAvailable.bind(this);
}
You can read in more detail about it here and here.
Because your onClick: .bind(this.props.details.league_slug)
what is this.props.details.league_slug actually?
bind will change the reference of this in getSeasonsAvailable (this will ref to this.props.details.league_slug, I don't know what it is), of course you will get undefined when you call this.props
Try just .bind(this), so the this in getSeasonsAvailable can ref to the component itself.

Resources