How to get an item from an array in JSON in ReactJS? - arrays

I am trying to get an item "icon" from "weather" form following JSON
{
"coord": {
"lon": 14.33,
"lat": 49.94
},
"weather": [{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}]
}
I can't figure out how to exctract an item which is in array through render method.
My code is:
class Weather extends React.Component {
constructor() {
super();
this.state = {
'items': []
}
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch('http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4')
.then(results => results.json())
.then(results => this.setState ({'items': results}));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>
})}
</div>
);
}
}
I actually used this question here: Get access to array in JSON by ReactJS ...which got me this far, but still can't make it working...

Your weather array is not set until your fetch request is complete, so this.state.items.weather.map in your render method will result in an error.
You could give weather an empty array as default value.
class Weather extends React.Component {
constructor() {
super();
this.state = {
items: {
weather: []
}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"http://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}

copy paste this example in codesandbox.io .you were initializing the items in constructor as array(where as fetch gave you an object) and for the initial render, items.weather was undefined and in render method you were trying to access map of undefined which was causing the error. (I have changed the url to https to run it in codesandbox)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component {
constructor() {
super();
this.state = {
items: {}
};
}
componentDidMount() {
this.getItems();
}
getItems() {
fetch(
"https://api.openweathermap.org/data/2.5/weather?lat=49.9415967&lon=14.3316786&appid=ed62e370682cc9e4144620905eff37e4"
)
.then(results => results.json())
.then(results => this.setState({ items: results }));
}
render() {
return (
<div>
<h1>here should be an icon..</h1>
{this.state.items.weather &&
this.state.items.weather.map(function(weather, index) {
return <h3 key={index}>{weather.icon}</h3>;
})}
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Related

Using ReactJs to fetch data from an api but getting a blank page with no error

Hello Guys kindly someone assist me with the issue i am having with my code. I am a newbie trying to learn react. i am trying to fetch data from an api. From the browser console i can see the data but when i try to return the data in the Dom i get a blank page. see my code below.
import React, { Component } from "react";
class FetchApi extends Component {
constructor(props) {
super(props);
this.state = {
person: []
};
}
componentDidMount() {
fetch("https://api.randomuser.me/")
.then(res => res.json())
.then(data => console.log(data))
.then(data => {
this.setState({
person: data
});
});
}
render() {
return (
<div>
{this.state.person &&
this.state.person.map(item => (
<li key={item.results.id}>
{item.results.gender} {item.results.location}
</li>
))}
</div>
);
}
}
export default FetchApi;
I have modified your code to the following. In some cases the way you are referencing the properties was wrong. Have made some changes in your componentDidMount and in the render method.
Sandbox for your reference: https://codesandbox.io/s/react-basic-example-nubu7
Hope this resolves
import React, { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
person: []
};
}
componentDidMount() {
try {
fetch("https://api.randomuser.me/")
.then(res => res.json())
.then(data => {
this.setState({
person: data.results
});
});
} catch (e) {
console.log(e);
}
}
render() {
return (
<div>
{this.state.person &&
this.state.person.map(item => (
<li key={item.id.value}>
{item.gender} {item.location.city}
</li>
))}
</div>
);
}
}
export default App;
Check this codesandbox, the response from the fetch API is in this format
{
"results":[
{
"gender":"male",
"name":{
"title":"Monsieur",
"first":"Alois",
"last":"Fernandez"
},
"location":{
"street":{
"number":1856,
"name":"Rue Duquesne"
},
"city":"Untereggen",
"state":"Genève",
"country":"Switzerland",
"postcode":9650,
"coordinates":{
"latitude":"-50.1413",
"longitude":"-23.6337"
},
"timezone":{
"offset":"+5:30",
"description":"Bombay, Calcutta, Madras, New Delhi"
}
},
"email":"alois.fernandez#example.com",
"login":{
"uuid":"04b2ee45-cf07-4015-a5d8-2311f6dc28a1",
"username":"ticklishleopard228",
"password":"forward",
"salt":"V8bDcgux",
"md5":"d521c6488fc4644ccb7e670e9e67bc20",
"sha1":"9673afe219f27817c573a9cb727c209357d386ef",
"sha256":"c13a5bc77dc720a6cc46bc640680e9501225fc94c9bc6ba7fe203fe989a992a0"
},
"dob":{
"date":"1957-11-24T13:46:29.422Z",
"age":63
},
"registered":{
"date":"2003-05-18T19:56:11.397Z",
"age":17
},
"phone":"077 871 56 07",
"cell":"077 461 83 98",
"id":{
"name":"AVS",
"value":"756.1050.9271.56"
},
"picture":{
"large":"https://randomuser.me/api/portraits/men/8.jpg",
"medium":"https://randomuser.me/api/portraits/med/men/8.jpg",
"thumbnail":"https://randomuser.me/api/portraits/thumb/men/8.jpg"
},
"nat":"CH"
}
],
"info":{
"seed":"76a6b875b2ba81dd",
"results":1,
"page":1,
"version":"1.3"
}
}
So you have to set the person in the state to data.results instead of data and access the item in the results accordingly.

Can't loop through array using map loop with react.js & axios

I am fetching data from an API, i get all the data in the console.log(this.state.reviews) but i can't access the data after the map loop is done.
The Brand data is accessible, the only problem is with the map loop (the reviews array)
I know it has something with async calls! i have found this Can't access values for Axios calls in map loop Does anyone know how to adapt it with the code below ;-)
class App extends React.Component {
constructor(props){
super(props);
this.state = {
reviews : []
};
}
componentDidMount() {
axios.get(`https://api.prime.com/businesses/reviews/${this.props.BrandId}`)
.then((res) => {
const brand = res.data;
this.setState({
// Reviews data
reviews : brand.reviews.items,
// Brand data
logo : brand.business.logo,
name : brand.business.name,
voters : brand.business.voters,
url : brand.business.url
});
})
}
render() {
console.log(this.state.reviews);
return (
<div className="widget-wrapper">
<OwlCarousel className="reviews-container"
loop
nav
margin={12}
dots={false}
items={5}
autoplay={true}
>
{this.state.reviews.map((review) => {
<div key={review.id} className="review-item">
<a href={this.state.url + '/' + review.slug} target="_blank" rel="nofollow">
<div className="review-heading">{review.subject}</div>
<div className="review-content">{review.message}</div>
</a>
</div>
})}
</OwlCarousel>
</div>
)
}
}
i figured out your problem you need to wait for the promise to resolve that why your state is not set for doing this u can use async await like
import React from "react";
import ReactDOM from "react-dom";
import axios from "axios";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentDidMount() {
this.loadData();
}
loadData = async () => {
const res = await axios.get("https://jsonplaceholder.typicode.com/posts");
if (res) {
const posts = res.data;
this.setState({
posts: [...posts]
});
}
};
renderPost = () => {
return this.state.posts
? this.state.posts.map(data => (
<div style={{ color: "black" }}>
<h5>{data.title}</h5>
<p>{data.body}</p>
</div>
))
: "loading...";
};
render() {
console.log(this.state.posts);
return <div> {this.renderPost()}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
full app is here check it out : codesandbox

static getDerivedStateFromProps does't behave like componentwillreceiveprops?

Hi I'm trying to implement search in child component , the parent component will get data from server and pass that data to child component
as props, now child component has to implement search on that data, I have used componentwillreceiveprops which is depreciated how can I implement
this without using componentwillreceiveprops, below is my code
working example on fiddle
class Parent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
// mimic api call
const data = [
{ key: 'react'}, { key: 'redux'},
{ key: 'javascript' }, { key: 'Ruby'} ,{key: 'angular'}
]
setTimeout(this.setState({data}), 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data = {this.state.data}/>
</React.Fragment>
)
}
}
class ChildComponent extends React.Component{
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentwillreceiveprops(nextProps){
this.setState({data: nextProps.data})
}
search(e){
console.log('props,', e.target.value)
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value)
})
this.setState({data: searchedData})
};
render(){
return(
<div>
search for (react, redux, angular, ruby)
<br/> <br/> <br/>
<input type = 'text' onChange={this.search.bind(this)}/>
{this.state.data.map(d => {
return (<div key={d.key}>{d.key}</div>)
})}
</div>
)
}
}
getDerivedStateFromProps is not a direct replacement for componentWillReceiveProps. Its just meant to update state in response to any update and unlike componentWillReceiveProps, getDerivedStateFromProps is triggered on every update either from child or from parent so you cannot simply update state without any conditional check. In order to update state if the props changed, you need to store the previous props in the state of child too or update the key of child so that it triggers a re-render
There are two possible approaches to this. Below is an example of first approach with getDerivedStateFromProps
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import "./styles.css";
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
// mimic api call
const data = [
{ key: "react" },
{ key: "redux" },
{ key: "javascript" },
{ key: "Ruby" },
{ key: "angular" }
];
setTimeout(() => {
this.setState({ data });
setTimeout(() => {
this.setState(prev => ({ data: [...prev.data, { key: "Golang" }] }));
}, 3000);
}, 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data={this.state.data} />
</React.Fragment>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
static getDerivedStateFromProps(props, state) {
if (!_.isEqual(props.data, state.prevData)) {
return {
data: props.data,
prevData: state.data
};
} else {
return {
prevData: props.data
};
}
}
search(e) {
console.log("props,", e.target.value);
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value);
});
this.setState({ data: searchedData });
}
render() {
return (
<div>
search for (react, redux, angular, ruby)
<br /> <br /> <br />
<input type="text" onChange={this.search.bind(this)} />
{this.state.data.map(d => {
return <div key={d.key}>{d.key}</div>;
})}
</div>
);
}
}
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
Working DEMO
Second approach involves changing the key of the child component instead of implementing getDerivedStateFromProps
import React from "react";
import ReactDOM from "react-dom";
import _ from "lodash";
import "./styles.css";
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
keyData: 0
};
}
componentDidMount() {
// mimic api call
const data = [
{ key: "react" },
{ key: "redux" },
{ key: "javascript" },
{ key: "Ruby" },
{ key: "angular" }
];
setTimeout(() => {
this.setState(prev => ({ data, keyData: (prev.keyData + 1) % 10 }));
setTimeout(() => {
this.setState(prev => ({
data: [...prev.data, { key: "Golang" }],
keyData: (prev.keyData + 1) % 10
}));
}, 3000);
}, 3000);
}
render() {
return (
<React.Fragment>
<ChildComponent data={this.state.data} key={this.state.keyData} />
</React.Fragment>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.data
};
}
search(e) {
console.log("props,", e.target.value);
let searchedData = this.props.data.filter(el => {
return el.key.startsWith(e.target.value);
});
this.setState({ data: searchedData });
}
render() {
return (
<div>
search for (react, redux, angular, ruby)
<br /> <br /> <br />
<input type="text" onChange={this.search.bind(this)} />
{this.state.data.map(d => {
return <div key={d.key}>{d.key}</div>;
})}
</div>
);
}
}
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);
Working DEMO
You can go ahead with the second method when you know that you will have quite a few updates in the child component whereas update from parent will be less frequent and the props that you have to compare getDerivedStateFromProps is nested . In such as case implementing getDerivedStateFromProps will be less performant than updating the key since you will need to perform expensive computation on each render.
To implement your componentWillReceiveProps() behavior using the new getDerivedStateFromProps() method, you can replace your current componentwillreceiveprops() hook with this:
static getDerivedStateFromProps(nextProps, state){
/* Return the new state object that should result from nextProps */
return { data : nextProps.data }
}
The getDerivedStateFromProps() will be called before your component is rendered and, if a non-null value is returned, then that return value will become the state of the component.
In your case, the state of the <ChildComponent> component has only one data field which is populated directly from props, so returning { data : nextProps.data } would be sufficent to update the data state field to match the incoming data prop.
The general idea is that you can use this method to update a component's state based on changing/incoming props.
See this documentation for more information on getDerivedStateFromProps() - hope that helps!
Update
Also on another note, it seems the way <Parent> is updating state via the setTimeout method is incorrect. You should update it as follows:
// Incorrect: setTimeout(this.setState({data}), 3000);
setTimeout(() => this.setState({data}), 3000);

React setState fetch API

I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.
My code
App.js
import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state =
{
value: '',
showComponent: false,
results: [],
images: {},
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleOnChange = this.handleOnChange.bind(this);
this.getImages = this.getImages.bind(this);
this.getData = this.getData.bind(this);
}
ComponentWillMount() {
this.getImages();
this.getData();
}
getImages(d) {
let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
images: data.images
});
});
}
getData() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
this.setState({
results: data.results
});
});
}
handleOnChange(e) {
this.setState({value: e.target.value})
}
handleSubmit(e) {
e.preventDefault();
this.getImages();
this.setState({showComponent: true});
this.getData();
}
render() {
return (
<SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
);
}
}
export default App;
SearchInput.js
import React, {Component} from 'react';
import MoviesList from './MoviesList';
class SearchInput extends Component {
render() {
return(
<div className='container'>
<form id='search-form' onSubmit={this.props.handleSubmit}>
<input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
<button type='submit'>Search</button>
</form>
<ul>
{this.props.showComponent ?
<MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
}
</ul>
</div>
)
}
}
export default SearchInput;
This is the component where I try to fetch details data
MovieList.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
let details = [];
class MoviesList extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
data.results.forEach((result, i) => {
let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
.then((response) => {
return response.json();
}).then((data) => {
details.push(data)
this.setState({details: details});
});
});
console.log(details);
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
console.log(this.props.results)
}
render() {
let results;
let images = this.props.images;
results = this.props.results.map((result, index) => {
return(
<li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
return (
<div>
{results}
<div>
{this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
</div>
</div>
)
}
}
export default MoviesList;
MovieDetails.js
import React, { Component } from 'react';
class MovieDetails extends Component {
render() {
let details;
details = this.props.details.map((detail,index) => {
if (this.props.results[index].id === detail.id) {
return(
<div key={detail.id}>
{this.props.results[index].id} {detail.id}
</div>
)} else {
console.log('err')
}
});
return(
<ul>
{details}
</ul>
)
}
}
export default MovieDetails;
Theres a lot going on here...
//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.
//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.
getDetails(id){
fetch(id)
displayresults, profit
}
results = this.props.results.map((result, index) => {
return(
<li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
{result.title}{result.id}
<img src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
</li>
)
});
Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component
Movielist
import React, { Component } from 'react';
import Movie from './Movie';
class MoviesList extends Component {
render() {
let results;
if(this.props.results) {
results = this.props.results.map((result, index) => {
return(
<Movie key={result.id} result={result} images={this.props.images}/>
)
});
}
return (
<div>
{results}
</div>
)
}
}
export default MoviesList;
Movie.js
import React, { Component } from 'react';
import MovieDetails from './MovieDetails';
class Movie extends Component {
constructor(props) {
super(props);
this.state = {
showComponent: false,
details: []
}
this.showDetails = this.showDetails.bind(this);
this.getDetails = this.getDetails.bind(this);
}
componentDidMount() {
this.getDetails();
}
getDetails() {
let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');
fetch(request)
.then((response) => {
return response.json();
}).then((data) => {
let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
return fetch(url)
}).then((response) => {
return response.json();
}).then((data) => {
this.setState({details: data});
});
}
showDetails(id) {
this.setState({showComponent: true}, () => {
console.log(this.state.details)
});
}
render() {
return(
<li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
{this.props.result.title}
<img src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
{this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
</li>
)
}
}
export default Movie;

Render happens before componentwillmount Meteor API call

Hello I'm having some trouble with async. It seems that the render is called before the api call is done in the componentwillmount
// Framework
import React, { PureComponent } from "react";
// Components
import Page from "../components/Page.jsx";
import Button from "../components/Button.jsx";
class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
order: null,
error: null
};
}
componentWillMount() {
Meteor.call("orders.getLastOrder", (error, response) => {
if (error) {
this.setState(() => ({ error: error }));
console.log(error);
} else {
this.setState(() => ({ order: response }));
console.log(this.state.order[0].name);
}
});
}
goBack = () => this.props.history.push("/shop");
goCart = () => this.props.history.push("/cart");
render() {
return (
<Page pageTitle="Cart" history goBack={this.goBack} goCart={this.goCart}>
<div className="home-page">
<div>
{this.state.order.map((item, i) => <div key={i}> {item.name}
{item.price} {item.quantity}</div>)}
</div>
<Button
onClick={() => {
this.props.history.push("/shop");
}}
>
Go shopping
</Button>
</div>
</Page>
);
}
}
export default Home;
I am having trouble trying to figure out how to loop through the objects from my state and display them in rows (I'm trying to create a cart)
0:{name: "TEMPOR Top", price: 875.5, quantitiy: 6}
1:{name: "CONSECTETUR Coat", price: 329.8, quantitiy: 3}
_id:"6RNZustHwbKjQDCYa"
You will still get the extra render but I assume you getting an error on the .map() function?
if you only change your constructor into this:
constructor(props) {
super(props);
this.state = {
order: [],
error: null
};
}
you won't get the error, because you can't use .map on a null object but you can use it on a empty array.

Resources