how I improve the asynchrony in this javascript code? - javascript

i have the next code in react, it requests information by api in monday.com, then with the answer I make another request for the weather by cities. then I render a climate card for each city found. the problem is that the array "cityWeathers" should have 9 elements, but some calls come 3, in another 5, in another 7, always several. I don't know what the error can be.
constructor(props) {
super(props);
this.state = {
setData: {},
context:{},
settings: {},
myData: { boards: [] },
cityWeathers:[]
};
}
componentDidMount() {
const getWeather = async () => monday.api('query { boards( ids : xxxxxxxxxx ) { items { id : name column_values { text }}}}')
.then((res) => {
this.setState({myData:res.data});
this.state.myData.boards.map((board) => {
board.items.map((item) =>{
fetch(`https://api.weatherapi.com/v1/current.json?key=xxxxxxxxxxxxxxxxxxxxxx=${item.column_values[3].text}&aqi=no`)
.then((res) => res.json())
.then((json) => {
//console.log(json);
let cityWeather ={
name: json.location.name,
temp_c:json.current.temp_c,
temp_f:json.current.temp_f,
condition: json.current.condition.text,
localTime: json.location.localtime,
icon: json.current.condition.icon
};
let cityWeathers = [...this.state.cityWeathers, cityWeather];
this.setState({cityWeathers});
})
})}
)
}
)
getWeather()
}
render(){
return(
<div className="App">
<div className="container">
<div className="row">
<div className="col-md-4">
{console.log(this.state.cityWeathers.length)}
{ this.state.cityWeathers.map((city) =>
<WeatherCard key={city.name} className="cards" name={city.name}
temp_c={city.temp_c} temp_f={city.temp_f} icon={city.icon}
condition={city.condition} localtime={city.localTime} />)
}
</div>
</div>
</div>
</div>
)
}
}
>I request information by api in monday.com, then with the answer I make another request for the weather by cities. then I render a climate card for each city found. the problem is that the array "cityWeathers" should have 9 elements, but some calls come 3, in another 5, in another 7, always several. I don't know what the error can be.

Related

Push object from JSON rest api into empty array not working in React

I am trying to push a object from a JSON response from a rest api into an empty array in React, so far my code is not working, I am fairly new to React so can't see where I am going wrong? Maybe something to do with the function in the state? I am getting error:
Cannot read property 'saved' of undefined
code so far:
import React, { Component } from 'react';
import './news-hero.css';
import Carousel from "react-multi-carousel";
import "react-multi-carousel/lib/styles.css";
const responsive = {
superLargeDesktop: {
breakpoint: { max: 4000, min: 3000 },
items: 1,
},
desktop: {
breakpoint: { max: 3000, min: 1024 },
items: 1,
},
tablet: {
breakpoint: { max: 1024, min: 464 },
items: 1,
},
mobile: {
breakpoint: { max: 464, min: 0 },
items: 1,
},
};
class NewsHero extends Component {
state = {
loading: false,
data: [],
headline: [],
saved: []
}
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
onError() {
this.setState({
imageUrl: "../assets/img-error.jpg"
})
}
componentDidMount() {
this.setState({ loading: true })
fetch('https://newsapi.org/v2/everything?q=timbaland&domains=rollingstone.com,billboard.com&excludeDomains=townsquare.media&apiKey=8')
.then(headline => headline.json())
.then(headline => this.setState({ headline: headline.articles, loading: false }, () => console.log(headline.articles)))
}
render() {
return (
<div className="hero">
<h2 className="text-left">News</h2>
{this.state.loading
? "loading..."
: <div>
<Carousel
additionalTransfrom={0}
showDots={true}
arrows={true}
autoPlaySpeed={3000}
autoPlay={false}
centerMode={false}
className="carousel-hero"
containerClass="container-with-dots"
dotListClass="dots"
draggable
focusOnSelect={false}
infinite
itemClass="carousel-top"
keyBoardControl
minimumTouchDrag={80}
renderButtonGroupOutside={false}
renderDotsOutside
responsive={responsive}>
{this.state.headline.map((post, indx) => {
return (
<div className="text-left mt-5" key={indx}>
<img className="media-img card-img-top card-img-hero" src={post.urlToImage} alt="Alt text"></img>
<div className="card-body container hero-text-body">
<h1 className="card-title hero-title text-truncate">{post.title}</h1>
<button className="btn-primary btn mt-2 mb-4" onClick={this.saved}>Add this article</button>
<p className="card-text">{post.description}</p>
Read More
</div>
</div>
)
})}
</Carousel>
</div>
}
</div>
)
}
}
export default NewsHero;
Any idea's and insight?
Cannot read property 'saved' of undefined
You are not correctly referencing this.state for saved or headline.
Cannot read property 'state' of undefined
Either add a constructor and bind this to your saved function.
constructor(props) {
super(props);
this.saved = this.saved.bind(this);
}
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
Or define saved as an arrow function to bind this
saved = () => {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
Should be
saved() {
const { headline, saved } = this.state;
this.setState({ saved: [...saved, headline] });
}
or
saved = () => {
const { headline, saved } = this.state;
this.setState({ saved: [...saved, headline] });
}
UPDATE: Save a specific post/headline
I see now, if you wish to save a specific headline then you need to update the signature of your saved function and how you call it.
saved = headline => {
this.setState(
prevState => ({ saved: [...prevState.saved, headline] })
);
}
and when you invoke it as a callback
<button
className="btn-primary btn mt-2 mb-4"
onClick={() => this.saved(post)} // pass the current post defined in the map callback
>
Add this article
</button>
One minor comment about naming, consistently referencing the same "object" the same way throughout code goes a long way in helping readability. I.E. how you reference headlines, posts, and articles. Pick one and be consistent.
this.state.headlines => this.state.headlines.map(headline => ...
this.state.posts => this.state.posts.map(post => ...
this.state.articles => this.state.articles.map(article => ...
saved() {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
You cannot defined class methods like this without writing a constructor like this:
class NewsHero extends Component {
constructor(props){
super(props)
this.saved = this.saved.bind(this)
}
...the rest of everything you wrote
}
Without doing this, your saved function is not bound to the instance of the class. Its silly, yes, but that's how class methods work in javasript. The other option is to use an arrow function, which preserves the context if this:
saved = () => {
this.saved.push(this.headline);
//alert('this is saved kind of');
}
I think this is faster and cleaner, and its pretty commonly used.
That is problem 1. Problem 2 is that it appears you are trying to modify the state object directly when writing this.saved.push(this.headline). That's not how to do it. To modify your state, you need to use the this.setState function:
saved = () => {
this.setState({
saved: whatever you want this to be
})
}
But it looks like you are already setting your state correctly in your fetch call, so I'm not sure what you're trying to accomplish with your saved function...?

Process a dictionary and return each entry in React

Basically I'm fetching data from my API, the data looks like this:
{
"comments": [
{
"user": "user1"
"text": "i'm a sample text1"
},
{
"user": "user2"
"text": "I'm a simple text2"
},
}
I want to use only the 2 comments['text'] entries and I want to render them to my HTML one by one, how should I achieve this?
componentDidMount() {
// Call REST API to get info on comments
fetch(this.props.url, { credentials: 'same-origin' })
.then((response) => {
//deal with response
})
.then((data) => {
this.setState({
comment_text : data['text'],
});
})
}
and this is how I wrote my render function:
render() {
// Render text for comments
return (
<div className="comments">
<p>{this.state.data}</p>
</div>
);
}
Use map as others said:
componentDidMount() {
// Call REST API to get info on comments
fetch(this.props.url, { credentials: 'same-origin' })
.then((response) => {
//deal with response
})
.then((data) => {
this.setState({
comment_text : data,
});
})
}
render() {
// Render text for comments
return (
<div className="comments">
{this.state.comment_text["comments"].map((value,key)=>{
return (<p>{value}</p>)})}
</div>
);
}
If your data is saved to this.state like this:
{
"comments": [
{
"user": "user1"
"text": "i'm a sample text1"
},
{
"user": "user2"
"text": "I'm a simple text2"
},
}
... and you want it to render like this:
<div className="comments">
<p>i'm a simple text1</p>
<p>I'm a simple text2</p>
</div>
.. then what you're looking for in your render function is:
render() {
return (
<div className="comments">
{this.state.data.comments.map((comment, index) => <p key={i}>{comment.text}</p> )}
</div>
);
}
keep your value to comments state and in render map loop through all ur data and set it in ur html. If it is a large html u better create a component for this Then u can call ur html inside render. I think this is pretty standard and clean way to render your dynamic data into ur view
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
comments: [
{
user: "user1",
text: "i'm a sample text1"
},
{
user: "user2",
text: "I'm a simple text2"
},
]
};
}
render() {
const d = this.state.comments.map(item => {
return (
<div>
<p>{item.user}</p>
<p>{item.text}</p>
</div>
)
})
return (
<div>{d}</div>
);
}
}
Hope this will help

Fetch API request to my React app

I'm trying to call an API to show information on my website in React.js, the API needs a token to be read, but I don't know what I have to do to generate the token, since my application doesn't need any register or login. It's like a catalog of products and you can personalize them. I don't understand very well this because I'm new and I'm learning all by myself, so I'm sorry if this is confusing, feel free to ask anything, I'll try to answer :)
Here's the code I have until now:
export class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
models: [],
isLoaded: false
};
}
componentDidMount() {
fetch(myUrlAPI)
.then(res => res.json())
.then(json => {
this.setState({
isLoaded: true,
models: json
});
});
}
render() {
const { isLoaded, models } = this.state;
if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
<ul>{models.map(model => <li>{model.name}</li>)};</ul>
<a href="/sofa">
<div className="Parcelas">
<img
src="../../img/image-card-1#2x.png"
className="ParcImage"
alt="sofa"
/>
<h1>Sofa hipnos</h1>
<h2>
1,200<span>€</span>
</h2>
<p className="Features">
w 20.5 x 15.5 x h 19.5 (cm)<br />Pele
</p>
<button className="Botao">
<p className="MostraDepois">See Details</p>
<span>+</span>
</button>
<img
src="../../img/points.svg"
className="Decoration"
alt="points"
/>
</div>
</a>
</div>
);
}
}
}
In the <ul> tag I was just trying to test the results from the json.
Also, everytime I use .map to make an array, I get this error (TypeError: models.map is not a function) or similar, can you tell me why?
Try this way and for token you need to confirm with the backend developer as how and what you should send.
let token = '1234567';
fetch(myUrlAPI, {
method: "GET",
headers: {
"authorization-key": token
}
}).then(res => res.json()
).then(function(json) {
this.setState({
isLoaded: true,
models: json
});
}, function(error) {
console.log(error)
})

Extract data from JSON array to ReactJs

I'm trying to extract data from JSON array using Reactjs. So far I've successfully extracted objects which are not inside array. But how can I extract items from array?
{this.state.data.stats.map(function (stat, i) {
return key={'stat-'+i}>{stat.base_stat}
this is wrong syntax... try this:
{this.state.data.stats.map(function (stat, i) {
return <div key={'stat-'+i}>{stat.base_stat}</div>});
}
I took a look at what you're trying to do and I think this is what you're trying to do, more or less:
class App extends React.Component {
constructor() {
super();
this.state = {
name: '',
height: '',
weight: '',
stats: [],
}
}
getData() {
return fetch('https://pokeapi.co/api/v2/pokemon/1.json')
.then((response) => response.json())
.then((responseJson) => {
const { name, height, weight, stats } = responseJson;
this.setState({ name, height, weight, stats });
})
.catch((error) => {
console.error(error);
});
}
componentDidMount() {
this.getData();
}
renderStats() {
return this.state.stats.map(s =>
<div key={s.stat.name}>
{s.stat.name}: {s.base_stat}
</div>)
}
render() {
return (
<div className="Info">
<div id="title" className="title">{this.state.name}</div>
<div id="parameters" className="parameters">
<tr><td>Height:</td> <td className="data">{this.state.height}</td></tr>
<tr><td>Weight:</td> <td className="data">{this.state.weight}</td></tr>
<br />
<div>Stats:</div>
<br />
</div>
<div id="stats" className="stats">
{this.renderStats()}
</div>
</div>
);
}
}
ReactDOM.render(<App />,
document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'/>
Couple of things:
You don't need to call setState multiple times. Batch your calls.
Make the this.state at least spell out the data it expects
Use a key that's meaningful if you can. You should only resort to using the array index position if you've nothing better. In this case I used the stat name.

The '.onChange' method doesn't get the newest info of 'Input' in React.js

import reqwest from './public/reqwest.js'
const PAGE_SIZE = 10
class App extends Component {
constructor() {
super()
this.state = {
page: 1,
arr: []
}
}
singleInfo(page) {
reqwest({
url: 'https://cnodejs.org/api/v1/topics',
data: {
limit: PAGE_SIZE,
page: this.state.page
},
success: function (data) {
this.setState({
arr: data.data
})
}.bind(this)
})
}
changeState(newState) {
this.setState(newState)
this.singleInfo(newState.page)
}
render() {
return (
<div>
<Menu val={this.state.page} changeParentState={(state) => this.changeState(state)} />
<List arr={this.state.arr} />
</div>
);
}
}
class Menu extends Component {
handleChange(event) {
if(event.target.value) {
this.props.changeParentState({
page: event.target.value
})
}
}
render() {
console.log(this)
return <input type="text" defaultValue={this.props.val} onChange={(event) => this.handleChange(event)} />
}
}
class List extends Component {
render() {
return <ul>
{
this.props.arr.map((ele) => {
return (
<li key={ ele.id }>
<p className="title">{ ele.title }</p>
<p className="date">{ ele.create_at }</p>
<p className="author">{ ele.author.loginname }</p>
</li>
)
})
}
</ul>
}
}
I can't get the current value of the input by onChange in Menu module.
In my code, the App has two child components - List & Menu.
You can input the page in Menu component, so it will send Ajax() to get the info of the page. But the truth is: After I change the value of input like 1 -> 10, the ajax get the value of 1.
Before this, I know the difference between keyup or keydown and keypress. They have the difference cross browser. But I just want get the current value of the input By React.js.
First, change:
<input type="text" defaultValue={this.props.val} onChange={(event) => this.handleChange(event)} />
To:
<input type="text" value={this.props.val} onChange={(event) => this.handleChange(event)} />
so that your input will update to the correct value on re-render.
Second, remember that setState is often asynchronous. So do not expect the state to be changed right after calling setState.
Your changeState method is good in this respect since it passes newState to singlePageRequest. However singlePageRequest does not use the supplied value and instead uses this.state.page. Change it to use the supplied value and you should be OK:
singleInfo(page) {
reqwest({
url: 'https://cnodejs.org/api/v1/topics',
data: {
limit: PAGE_SIZE,
page: page
},
success: function (data) {
this.setState({
arr: data.data
})
}.bind(this)
})
}

Categories

Resources