Reputation: 235
I am learning ReactJS. In my program, I am making an API call and later mapping it. The data which is fetched by API call is like, data = [{"uid":"1", "title":"hello"},{"uid":"2", "title":"World"}]
import ImporterAPI from '../api';
const API = new ImporterAPI();
class Home extends Component {
constructor(props) {
super(props);
this.state = {
data: ''
}}
componentWillMount() {
this.setState({ data: API.getData()}, () => {
console.log("data fetched");
var mapData = []
this.state.data.map( (object, i) => {
mapData.push(<p key={i}>{object}</p>)
})
})
}
render() {
return (
<div className="home">
{this.mapData}
</div>
)
}
}
And my API file,
import axios from 'axios';
class API {
getData = () => {
axios.get('http://localhost:8005/data')
.then(function (response) {
if (response.status === 200 && response != null) {
var data = response.data;
console.log(data)
return data
} else {
console.log('problem');
}
})
.catch(function (error) {
console.log(error);
});
}
}
My console.log is printing the data from API call and then I'm returning the data. Where in home component data is assigned using setState. But there is no data storing into this.state.data. It always remains undefined and I got error "TypeError: Cannot read property 'map' of undefined".
Please guide me. How should I print API call data & I would also like to know if this program is good or bad in terms of performance for making API calls or any other better way to improve performance. Thanks.
I would appreciate help.
Upvotes: 4
Views: 30346
Reputation: 33994
Do you really need another class for getting api data? Not required
Also componentWillMount method is deprecated so I would recommend you to move your axios code to componentDidMount method within the class.
Also initialize the data with empty array instead of string. And set the api response data to state i.e., data
Do map directly in render and display data.
Use arrow function in axios .then and .catch like I did in the below code otherwise this won’t be available to access state or props. You need to bind every .then and .catch otherwise
Your code can be simplified something like below
class Home extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
axios.get('http://localhost:8005/data')
.then(response => {
if (response.status === 200 && response != null) {
this.setState({
data: response.data
});
} else {
console.log('problem');
}
})
.catch(error => {
console.log(error);
});
}
render() {
const { data } = this.state;
return (
<div className="home">
{Array.isArray(data) && data.map(object => (
<p key={object.uid}>{object.title}</p>
))}
</div>
)
}
}
Upvotes: 3
Reputation: 114
There're two problems in your code.
First, API.getData()
is an async function. It means when you call API.getData()
, the data is not return intermediately (think like it takes few milliseconds to get the data). You should setState
after you fetched the data.
Secondly, you should send render logic in render
function.
It should look like this:
import React, { Component } from 'react'
import ImporterAPI from '../api'
const API = new ImporterAPI()
class Home extends Component {
constructor(props) {
super(props)
this.state = {
data: []
}
}
componentWillMount() {
API.getData().then(response => {
console.log('Data fetched', response)
this.setState({
data: response
})
})
}
render() {
return (
<div className="home">
{this.state.data.map((object, index) => (
<p key={index}>{object}</p>
))}
</div>
)
}
}
As @Askiron answer, you also should return axios....
in your API function.
Edit 2: Here's the better API, which return data in error case, so you don't get this.state.data is not a function
:
import axios from 'axios'
class API {
getData = () => {
return axios
.get('http://localhost:8005/data')
.then(function(response) {
if (response.status === 200 && response != null) {
var data = response.data
return data
} else {
throw new Error('Empty data')
}
})
.catch(function(error) {
console.log(error)
return [] // Return empty array in case error response.
})
}
}
Upvotes: 2