Create New Line after each Array Element - reactjs

I'm very new to react and trying to teach it to myself. Trying to make an API call and loop through the records. I've been successful so far, however, I can't seem to figure out how to create a new line after each element that gets displayed. I'm sure it's something simple that I'm just missing. Can someone advise here?
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
isLoaded: false,
}
}
async componentDidMount() {
const url = "https://api.randomuser.me/?results=10";
const response = await fetch(url);
const data = await response.json();
this.setState({ person: data, loading: false, len: data.results.length });
}
render() {
let items = [];
for (let i = 0; i < this.state.len; i++) {
const item = this.state.person.results[i].name.title;
items.push(item);
}
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{items}
</div>
)}
</div>
);
}
}

You can use .map() function to loop over the array display it in within the tags.
return (
<div>
{this.state.loading || !this.state.person ? (
<div>loading...</div>
) : (
<div>
{
items.map((item) => (
<>
<span>{item}</span>
<hr />
</>
)
}
</div>
)}
</div>
);

Related

Render data from async method is undefined

I'm trying to render data from my MongoDB document but I can't figure out why the state is undefined when it's an async function and I'm awaiting it.
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
offers: null
};
this.getOffers = this.getOffers.bind(this);
this.renderOffer = this.renderOffer.bind(this);
}
componentDidMount() {
if(!this.state.offers) {
this.getOffers();
}
}
async getOffers() {
let res = await offerService.getAll();
this.setState({ offers: res });
}
renderOffer(product) {
return (
<li key={product._id} className="list__item product">
<h3 className="product__name">{product.title}</h3>
</li>
);
};
render() {
return (
<div className="App">
<Nav />
<ul className="list">
{(this.state.offers && this.state.offers.length > 0) ? (
this.state.offers.map(offer => this.renderOffer(this.state.offer))
) : (
<p>Loading...</p>
)}
</ul>
</div>
);
}
}
Whenever I load the page, it returns TypeError: Cannot read property '_id' of undefined, despite the fact that if I log the output of res in getOffers() it shows the data. Why is this?
The problem lies in your map call:
this.state.offers.map(offer => this.renderOffer(this.state.offer))
You're attempting to use the undefined variable this.state.offer rather than your offer map variable. What you actually want is:
this.state.offers.map(offer => this.renderOffer(offer))

Updated State not being retrieved by method

In this file, I'm pulling in a bunch of data from an API and assigning it to the array of "baseData". In another file, I have an onkeyup event calling the generateResults method. But then generateResults method is only getting the original, blank state of the array. I'm new to react so any help is appreciated. Thanks!
import React from 'react';
import axios from 'axios';
export class LINKS extends React.Component{
constructor(props){
super(props);
this.state={
baseData: []
}}
getBaseData(){
axios.get("http://localhost:3000/api/links")
.then(response => {
this.setState({baseData: response.data});
}).catch((error) => {
console.error(error);
});
}
componentDidMount(){
this.getBaseData();
}
generateResults(){
var linkInfo = this.state.baseData
var searchBar = document.getElementById('searchBar')
console.log(linkInfo)
for(var i = 0; i < linkInfo.length; i ++){
}
}
render(){
var linkInfo = this.state.baseData
// console.log(linkInfo)
if(linkInfo.length === 0){
return(
<div><h1> Loading... </h1></div>)
} else {
return(
<div>{linkInfo.map((info, i) =>
<div>
<u>{info['client']}</u>
{info.links.map((link, i) =>
<div> {link.linkTitle}
<br/> {link.url} </div>) }
<hr/></div>
)}</div>
)
}
}
}
Method in the other class calling the generateResults method.
handleSearch(){
let links = new LINKS
links.generateResults()
}
Because you should return any HTML into your arrow function map loop with double Parentheses like this :
<ul>
{
this.props.items.map((item) => (
<li>{item}</li>
))
}
</ul>
I've edited your code, try this :
<div>
{
linkInfo.map((info, i) => (
<div>
<u>{info['client']}</u>
{
info.links.map((link, i) => (
<div> {link.linkTitle}
<br/> {link.url} </div>
))
}
<hr/></div>
))
}
</div>

React: component subscribing to redux store changes

I am currently trying to use react-redux to subscribe to store changes for newsItems changes. My current component which is working but not listening for store changes as it should be looks like:
class Home extends Component {
state = { loading: false };
displayName = Home.name
render() {
let contents = this.renderContents(store.getState().newsItems);
return (
<div>
{contents}
</div>
);
}
renderContents = (newsItems) => {
var largeNewsItems = newsItems.splice(0, 2);
var firstNewsItem = largeNewsItems[0];
var secondNewsItem = largeNewsItems[1];
return (
<div>
<div>
<LargeNewsItem newsItem={firstNewsItem} />
</div>
<div>
<LargeNewsItem newsItem={secondNewsItem} />
</div>
</div>
);
}
}
export default Home;
when trying to update it to subscribe to teh store, I've made the following attempt:
class Home extends Component {
state = { loading: false };
displayName = Home.name
render(props) {
let contents = this.renderContents(props.newsItems);
return (
<div>
{contents}
</div>
);
}
renderContents = (newsItems) => {
var largeNewsItems = newsItems.splice(0, 2);
var firstNewsItem = largeNewsItems[0];
var secondNewsItem = largeNewsItems[1];
return (
<div>
<div>
<LargeNewsItem newsItem={firstNewsItem} />
</div>
<div>
<LargeNewsItem newsItem={secondNewsItem} />
</div>
</div>
);
}
}
const mapStateToProps = function(state) {
return {
newsItems: state.newsItems
}
}
export default connect(mapStateToProps)(Home);
which results in error:
TypeError: Cannot read property 'newsItems' of undefined
where I call props.newsItems.
What am I doing wrong here and how can I overcome it?
UPDATE:
it looks like I can overcome it by using:
`render() {
let contents = this.renderContents(this.props.newsItems);
return (
<div>
{contents}
</div>
);
}`
however my LargeNewsItem components will be passed null data once in a while. How can I overcome this and essentially "wait" until newsItems is populated
One approach could be to replace your render method as below .
`
render() {
const newsItems = this.props.newsItems;
if(!newsItems) {
return null;
}
let contents = this.renderContents(this.props.newsItems);
return (
<div>
{contents}
</div>
);
}`
this way if your newsitems is null you wont get an error and once the newsitems uddates your render method will be called again

How to fix recursively updating state?

I am bulding an app using newsapi. i am facing two issue on my state. i fetch data using api and assign it to my state. and use it in my view.
Issue no 1
My view gets rendered before my app receives the data.
Issue no 2
When I try to update my state after a new fetch. it recursively updates the set of data again and again.
import React, {Component} from 'react';
import NewsComponent from './NewsComponent/NewsComponent'
class News extends Component {
state = {
displayStatus: false,
newsItems: []
};
toogleDisplayHandler = () => {
if(this.state.displayStatus===true){
this.setState({displayStatus:false})
}
else{
this.setState({displayStatus:true})
}
}
render(){
const NewsAPI = require('newsapi');
const newsapi = new NewsAPI('d6da863f882e4a1a89c5152bd3692fb6');
//console.log(this.props.keyword);
newsapi.v2.topHeadlines({
sources: 'bbc-news,abc-news',
q: this.props.keyword
}).then(response => {
//console.log(response)
response.articles.map(article => {
//console.log(article);
return(
//console.log(this.state.newsItems)
this.setState({
newsItems: [...this.state.newsItems, article],
})
//this.state.newsItems.push(article)
)
});
});
let Article = null;
Article = (
<div>
{
this.state.newsItems.map((news, index) => {
return (
<NewsComponent key={index}
title={news.title}
url={news.url}
description={news.description}
author={news.author}
publish={news.publishedAt}
image={news.urlToImage}
/>
)
})
}
</div>
)
return (
<div className="App">
{Article}
<button onClick={this.toogleDisplayHandler}>
{this.state.displayStatus === true ? "Hide Article" : "Display Articles"}
</button>
</div>
)
}
}
export default News;
Please help me to resolve this issue.
You should never setState in render as that would cause an infinite loop. Do it in componentDidMount or the constructor.
I would also recommend not using map for simply iterating over a list. Array.map is a function that is useful for returning an array that is constructed by iterating over another array. If you want to run some code for each element of an array use Array.forEach instead.
Like this:
import React, { Component } from "react";
import NewsComponent from "./NewsComponent/NewsComponent";
class News extends Component {
state = {
displayStatus: false,
newsItems: []
};
toogleDisplayHandler = () => {
if (this.state.displayStatus === true) {
this.setState({ displayStatus: false });
} else {
this.setState({ displayStatus: true });
}
};
componentDidMount = () => {
const NewsAPI = require("newsapi");
const newsapi = new NewsAPI("d6da863f882e4a1a89c5152bd3692fb6");
newsapi.v2
.topHeadlines({
sources: "bbc-news,abc-news",
q: this.props.keyword
})
.then(response => {
response.articles.forEach(article => {
this.setState({
newsItems: [...this.state.newsItems, article]
});
});
});
};
render() {
let Article = null;
Article = (
<div>
{this.state.newsItems.map((news, index) => {
return (
<NewsComponent
key={index}
title={news.title}
url={news.url}
description={news.description}
author={news.author}
publish={news.publishedAt}
image={news.urlToImage}
/>
);
})}
</div>
);
return (
<div className="App">
{Article}
<button onClick={this.toogleDisplayHandler}>
{this.state.displayStatus === true
? "Hide Article"
: "Display Articles"}
</button>
</div>
);
}
}
export default News;
1) You can add a check either your state has the data which you want to show on screen to render the view.
2) Please use ComponentDidMount React life cycle function to fetch data from an external source and update this data in the state. In the Render method, it will keep calling it recursively.

React function - is not defined no-undef

I get the following error when trying to compile my app 'handleProgress' is not defined no-undef.
I'm having trouble tracking down why handleProgress is not defined.
Here is the main react component
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
this.handleProgress = this.handleProgress.bind(this);
}
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
handleProgress = () => {
console.log('hello');
};
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
Your render method is wrong it should not contain the handlePress inside:
You are calling handlePress on this so you should keep it in the class.
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
this.handleProgress = this.handleProgress.bind(this);
}
handleProgress = () => {
console.log('hello');
};
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
If you are using handleProgress inside render you have to define it follows.
const handleProgress = () => {
console.log('hello');
};
if it is outside render and inside component then use as follows:
handleProgress = () => {
console.log('hello');
};
If you are using arrow function no need to bind the function in constructor it will automatically bind this scope.
handleProgress should not be in the render function, Please keep functions in you component itself, also if you are using ES6 arrow function syntax, you no need to bind it on your constructor.
Please refer the below code block.
class App extends Component {
constructor(props) {
super(props);
this.state = {
progressValue: 0,
};
// no need to use bind in the constructor while using ES6 arrow function.
// this.handleProgress = this.handleProgress.bind(this);
}
// move ES6 arrow function here.
handleProgress = () => {
console.log('hello');
};
render() {
const { questions } = this.props;
const { progressValue } = this.state;
const groupByList = groupBy(questions.questions, 'type');
const objectToArray = Object.entries(groupByList);
return (
<>
<Progress value={progressValue} />
<div>
<ul>
{questionListItem && questionListItem.length > 0 ?
(
<Wizard
onChange={this.handleProgress}
initialValues={{ employed: true }}
onSubmit={() => {
window.alert('Hello');
}}
>
{questionListItem}
</Wizard>
) : null
}
</ul>
</div>
</>
);
}
}
Try this one, I have check it on react version 16.8.6
We don't need to bind in new version using arrow head functions. Here is the full implementation of binding argument method and non argument method.
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0
};
constructor() {
super();
}
render() {
return (
<div>
<button onClick={this.updateCounter}>NoArgCounter</button>
<button onClick={() => this.updateCounterByArg(this.state.count)}>ArgCounter</button>
<span>{this.state.count}</span>
</div>
);
}
updateCounter = () => {
let { count } = this.state;
this.setState({ count: ++count });
};
updateCounterByArg = counter => {
this.setState({ count: ++counter });
};
}
export default Counter;

Resources