React Multiple Fetch from state - reactjs

so I am trying to fetch my SWAPI json-server twice using the homeworld id to get the homeworld name but I just get "TypeError: Cannot read property 'name' of undefined". Im fairly new to React so if it looks messy im sorry! Thanks in advance!
import React, { Component } from 'react';
import './Card.css';
const person = personID =>
`http://localhost:3008/people/${personID}`
const picture = pictureID =>
`http://localhost:3008/${pictureID}`
// const planet = planetID =>
// `http://localhost:3008/planets/${planetID}`
class Card extends Component {
constructor(props){
super(props);
this.state ={
requestFailed: false,
person: 1,
planet: 4
}
}
componentDidMount(){
fetch(person(this.state.person))
.then(response => {
if(!response.ok) {
throw Error("Network Request Failed");
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
cardData: d
})
}, () => {
this.setState({
requestFailed: true
})
})
fetch(`http://localhost:3008/planets/${this.state.cardData.homeworld}`)
.then(data => data.json())
.then(data => {
this.setState({
homeData: data
})
})
}
render() {
if(this.state.requestFailed === true) return <p>Error please try
again!</p>
if(!this.state.cardData) return <p>Loading ...</p>
return (
<div className='card'>
<div className='card-content'>
<div className='card-name'>{this.state.cardData.name}</div>
<img src={picture(this.state.cardData.image)}
alt='profile'/>
<p>
<span>Birthday:</span>
<span>{this.state.cardData.birth_year}</span>
</p>
<p>
{/* Note that in order to get the homeworld's name, you have to get the planet name from a different endpoint than the people */}
<span>Homeworld:</span>
<span>{this.state.homeData.name}</span>
</p>
</div>
</div>
);
}
}
export default Card;

It looks like the first problem is that you're attempting to render homeData.name before it's loaded. You should probably have a loading check similar to the cardData loading check. Or you could just render nothing until it's loaded:
{this.state.homeData ?
<span>{this.state.homeData.name}</span> :
<span>Loading...</span>
}
The 2nd problem is that you're doing the second fetch of homeData at the same time as the first fetch. So this line:
fetch(`http://localhost:3008/planets/${this.state.cardData.homeworld}`)
Will always fail because the cardData is not loaded into the state yet when it runs.
What you should do is move it inside the response part of the first request:
fetch(person(this.state.person))
.then(response => {
if(!response.ok) {
throw Error("Network Request Failed");
}
return response
})
.then(d => d.json())
.then(d => {
this.setState({
cardData: d
})
fetch(`http://localhost:3008/planets/${d.homeworld}`)
.then(data => data.json())
.then(data => {
this.setState({
homeData: data
})
})
}, () => {
this.setState({
requestFailed: true
})
})
It would help to extract these into separate functions to clean it up a bit.

Related

React state not updated and error "Can't perform a React state update on an unmounted component"

I'm trying to create an app that once a user logs in (enters a partyID), it directs them to a new page where it pulls back all the users data. Once they 'Log In; the new page isn't pulling the data as I expected.
However when I write to console the data is says undefined but the fetch URL does work when i go to it locally on my browser.
enter image description here
Here is my code
class CalcForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
}
componentDidMount() {
this.setState({ isLoading: true });
const serachByCustomerId = this.props.location.state;
const url =
"<MYURL>/api/Customer/" +
serachByCustomerId;
console.log("URL Being used ", url);
fetch(url)
.then((res) => res.json())
.then((data) => this.setState({ data: data }))
if (!this.state.isLoading) {
console.log("data after search", this.state.data);
}
}
// renders to display on page
render() {
const { data, isLoading } = this.state;
// if page is loading displays loading text and spinner to make user awear
if (isLoading) {
return (
<div className="pageLoading">
<p>Loading...</p>
<FadeLoader size={150} color={"#2d8259"} loading={isLoading} />
</div>
);
}
return (
<div> hi </div>
);
}
}
export default CalcForm;
I was expected the data returned to be printed into the console but upon looking I get undefined and there is also an error I don't understand
setState is asynchronous, so if you want to console.log the data, it must be within a callback:
this.setState({key: value}, () => {console.log(value})
This is what your componentDidMount() would look like:
componentDidMount() {
this.setState({isLoading: true });
const searchByCustomerId = this.props.location.state;
const url = "<MYURL>/api/Customer/" + searchByCustomerId;
console.log("URL Being used ", url);
fetch(url)
.then((res) => res.json())
.then((data) => this.setState({data: data },
() => {
console.log("data after search", data);
this.setState({isLoading: false})
}
))
}
PLUS: you had a typo (search not serach)
Hope this helps :)
Why not go down the hooks approach? Its far more nicer and easier to do things:
psuedo code to get you going. It has an await function so you should be able to derive your data once you pass in your url.
export default function CalcForm() {
const [isLoading, setLoading] = React.useState(true);
const [data, setData] = React.useState(false);
const getData = async () => {
setLoading(true);
const response = await fetch(url);
setData(response.json());
setLoading(false);
};
React.useEffect(() => {
getData();
}, []);
if (isLoading) {
return (
<div className="pageLoading">
<p>Loading...</p>
<FadeLoader size={150} color="#2d8259" loading={isLoading} />
</div>
);
}
return <div className="pageLoading">hi</div>;
}

React - get data with axios

In my react app that is based on class components, My response API got from open weather fixes after several lags.
this is my state
class Weather extends Component {
constructor(props) {
super(props);
this.state = {
weatherData: undefined,
weatherDescription: undefined,
};
}
My thinking was that when my componentDidMount,
weather API getting from openWeather and set it in state
componentDidMount() {
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=someCityId&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
and I want to update data when the city is changing, in componentDidUpdate the data get again from the openWeather
componentDidUpdate() {
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=someCityId&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
}
But the problem is that when my response receives, it faces a lag that causes data jumps several times to previous data and new data until it fixes
I do not completely understand the question, but this 'lags' because the action of fetching something from an external source is async and needs time to complete.
As for the second 'part' of displaying the loading text you have to set a variable (preferably in state which indicates the loading state of this component)
eg.
constructor(props) {
super(props);
this.state = {
loading: false,
airConditionsText: null,
// Other stuff you have in state
};
}
componentDidUpdate() {
this.setState({loading: true}) // Start of loading
axios
.get(
`http://api.openweathermap.org/data/2.5/weather?id=${this.state.inputId}&units=metric&appid=myApiKey`
)
.then((response) => {
if (response.request.status === 200) {
this.setState({
weatherData: response.data.main.temp,
weatherDescription: response.data.weather[0].description,
weatherTextDisplay: this.state.airConditionsText.filter((item)=>{
return item["id"] === response.data.weather[0].id
})
});
}else{throw Error('No internet')}
})
.catch(error => Error.message)
.finally(() => this.setState({loading: false})) // End of loading
.finally is being trigger once the async operation (fetching the data from weatherAPI) finishes with either error or success which is the time to stop loading.
Then you can use this.state.loading in component render to show loading text
eg.
render() {
return (
<div>
{this.state.loading
? <div> Loading... </div>
: <div>{this.state.airConditionsText}</div> // other stuff you want to display
}
</div>
);
}

JSON Data Object Handling in React

Cant figure it out how to extract data from parsed and stored json object from state ... i have tried many ways but getting errors (data from openweathermap)
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: null
};
}
getWeather(url) {
fetch(url)
.then(res => res.json())
.then(data =>
this.setState({
isLoading: false,
data: data
})
)
.catch(error => console.log("Error Loading data " + error));
}
componentDidMount() {
navigator.geolocation.getCurrentPosition(position => {
const url = `${API}lat=${position.coords.latitude}&lon=${position.coords.longitude}&${APPID}`;
this.getWeather(url);
});
}
render() {
return (
<div>
{console.log(this.state.data)}
{/*renders json*/}
<h1>{this.state.data.name}</h1>
</div>
);
}
}
I believe you might be getting cannot read property 'name' of null. Not sure about the exact error but something like that.
Try to use
<h1>{ this.state.data && this.state.data.name }</h1>
Till the time API is not giving the response, your data in the state is null so you cannot access data.name.

TypeError: this.state.patients.map is not a function

i am new in react js,and i am learning to create a React application and I got a problem with mapping function:
Here's my request and how I am attempting to render the data:
class Patients extends Component {
constructor(props) {
super(props)
this.state = {
patients: []
}
}
componentDidMount() {
api.getPatients()
.then( patients => {
console.log( patients)
this.setState({
patients: patients
})
})
.catch(err => console.log(err))
}
render() {
return (
<div className=" Patientss">
<h2>List of Patient</h2>
{this.state.patients.map((c, i) => <li key={i}>{c.name}</li>)}
</div>
);
}
}
export default Patients;
here my api calling
import axios from 'axios';
const service = axios.create({
baseURL: process.env.NODE_ENV === 'production' ? '/api' : 'http://localhost:3000/patient',
});
const errHandler = err => {
console.error(err);
throw err;
};
export default {
service: service,
getPatients() {
return service
.get('/')
.then(res => res.data)
.catch(errHandler);
},
}
and I get the following error:
TypeError: this.state.patients.map is not a function
i've try to use slice aswell but it didnt work, anyone know whats wrong with my code?`
Based on the symptoms (heh), the patients object you get in api.getPatients() isn't an array.
console.log() it to see what it actually is.
EDIT: Based on the comments, the patients object looks like
{
count: 24,
patient: [...],
}
so the this.setState() call needs to be
this.setState({patients: patients.patient})
You can also do something like this as an conditional rendering. It will check that if this.state.patient exists then only it will go ahead and call this.state.patients.map function. It will also ensure that you don't receive any errors later on due to bad responses.
I updated your Patients Code example.
class Patients extends Component {
constructor(props) {
super(props)
this.state = {
patients: []
}
}
componentDidMount() {
api.getPatients()
.then( patients => {
console.log( patients)
this.setState({
patients: patients
})
})
.catch(err => console.log(err))
}
render() {
return (
<div className=" Patientss">
<h2>List of Patient</h2>
{ this.state.patients && this.state.patients.map((c, i) => <li key={i}>{c.name}</li>)}
</div>
);
}
}
export default Patients;
I hope it helps. Thanks!!

Undefined object in react js

I'm getting the following error:
Uncaught ReferenceError: videos2 is not defined
In this app:
class App extends Component {
constructor(props) {
super(props)
this.state = {
videos2:[],
selectedVideo:null
}
this.DMSearch()
}
DMSearch(){
fetch("https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars")
.then(response => response.json())
.then(data=>this.setState({
videos2:data.videos2,
selectedVideo:videos2[0]}))
console.log(videos2)
}
render () {
const {videos2}=this.state
return (
<div>
<SearchBar onSearchTermChange= {DMSearch}/>
<VideoDetail video={this.state.selectedVideo}/>
<VideoList
onVideoSelect={selectedVideo=>this.setState({selectedVideo})}
videos2={this.state.videos2}/>
</div>
)
}
}
Therefore Im wondering where should I define videos2 apart from where it is defined already. Anyone could point me out to the part of the component that might be causing the error?
EDIT: Actually it had to do with the way api json was shaped.
This is the proper way to fetch the list from json:
this.setState({
videos2: videos2.list,
selectedVideo: videos2[0]
});
In DMSearch function there is no videos2 variable defined. You probably want:
.then(data => {
this.setState({
videos2: data.videos2,
selectedVideo: data.videos2[0]
});
console.log(data.videos2);
});
or
.then(data => {
const { videos2 } = data;
this.setState({
videos2,
selectedVideo:videos2[0]
});
console.log(videos2);
});
In DMSearch, videos2 is undefined.
DMSearch() {
fetch("https://api.dailymotion.com/videos?fields=description,id,thumbnail_url,title,&limit=5&search=cars")
.then(response => response.json())
.then(data => {
let videos2 = data.videos2; //define it here
this.setState({
videos2: videos2,
selectedVideo: videos2[0] // <-- this line will throw error
})
})
console.log(videos2) // <-- and this too
}

Resources