Reputation: 661
I'm new to reactjs, I want to fetch data in server, so that it will send page with data to client.
It is OK when the function getDefaultProps return dummy data like this {data: {books: [{..}, {..}]}}.
However not work with code below. The code execute in this sequence with error message "Cannot read property 'books' of undefined"
However, I expect the code should run in this sequence
Any Idea?
statics: {
fetchData: function(callback) {
var me = this;
superagent.get('http://localhost:3100/api/books')
.accept('json')
.end(function(err, res){
if (err) throw err;
var data = {data: {books: res.body} }
console.log('fetch');
callback(data);
});
}
getDefaultProps: function() {
console.log('getDefaultProps');
var me = this;
me.data = '';
this.fetchData(function(data){
console.log('callback');
console.log(data);
me.data = data;
});
console.log('return');
return me.data;
},
render: function() {
console.log('render book-list');
return (
<div>
<ul>
{
this.props.data.books.map(function(book) {
return <li key={book.name}>{book.name}</li>
})
}
</ul>
</div>
);
}
Upvotes: 62
Views: 177946
Reputation: 33
If you are using class based react components, then fetch the data inside the componentWillMount() method, if you are using functional components use the useeffect hook, everything inside it will load before mount and everything inside the return statement will mount afterwards.
using useEffect hook:
import React, { useEffect, useState } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
const response = await fetch('your-api-endpoint');
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
};
// Render the component using the fetched data
return <div>{/* Render your component with the fetched data */}</div>;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
using componentDidMount():
import React from 'react';
class MyComponent extends React.Component {
state = {
data: [],
};
async componentDidMount() {
try {
const response = await fetch('your-api-endpoint');
const result = await response.json();
this.setState({ data: result });
} catch (error) {
console.error('Error fetching data:', error);
}
}
// Render the component using the fetched data
render() {
return <div>{/* Render your component with the fetched data */}</div>;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
Upvotes: 1
Reputation: 21
Try using componentDidMount
:
componentDidMount : function () {
// Your code goes here
},
If you are using hooks, use the useEffect
hook:
useEffect(() => {
// Your code goes here
});
Documentation on useEffect
Upvotes: 0
Reputation: 4543
I've just stumbled upon this problem too, learning React, and solved it by showing spinner until the data is ready.
render() {
if (this.state.data === null) {
return (
<div className="MyView">
<Spinner/>
</div>
);
}
else {
return(
<div className="MyView">
<ReactJson src={this.state.data}/>
</div>
);
}
}
Upvotes: 1
Reputation: 1488
You can use redial package for prefetching data on the server before attempting to render
Upvotes: 0
Reputation: 18803
A very simple example of this
import React, { Component } from 'react';
import { View, Text } from 'react-native';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data : null
};
}
componentWillMount() {
this.renderMyData();
}
renderMyData(){
fetch('https://your url')
.then((response) => response.json())
.then((responseJson) => {
this.setState({ data : responseJson })
})
.catch((error) => {
console.error(error);
});
}
render(){
return(
<View>
{this.state.data ? <MyComponent data={this.state.data} /> : <MyLoadingComponnents /> }
</View>
);
}
}
Upvotes: 21
Reputation: 299
The best answer I use to receive data from server and display it
constructor(props){
super(props);
this.state = {
items2 : [{}],
isLoading: true
}
}
componentWillMount (){
axios({
method: 'get',
responseType: 'json',
url: '....',
})
.then(response => {
self.setState({
items2: response ,
isLoading: false
});
console.log("Asmaa Almadhoun *** : " + self.state.items2);
})
.catch(error => {
console.log("Error *** : " + error);
});
})}
render() {
return(
{ this.state.isLoading &&
<i className="fa fa-spinner fa-spin"></i>
}
{ !this.state.isLoading &&
//external component passing Server data to its classes
<TestDynamic items={this.state.items2}/>
}
) }
Upvotes: 9
Reputation: 53
As a supplement of the answer of Michael Parker, you can make getData accept a callback function to active the setState update the data:
componentWillMount : function () {
var data = this.getData(()=>this.setState({data : data}));
},
Upvotes: 1
Reputation: 3115
Responded to a similar question with a potentially simple solution to this if anyone is still after an answer, the catch is it involves the use of redux-sagas:
https://stackoverflow.com/a/38701184/978306
Or just skip straight to the article I wrote on the topic:
Upvotes: 0
Reputation: 12966
What you're looking for is componentWillMount
.
From the documentation:
Invoked once, both on the client and server, immediately before the initial rendering occurs. If you call
setState
within this method,render()
will see the updated state and will be executed only once despite the state change.
So you would do something like this:
componentWillMount : function () {
var data = this.getData();
this.setState({data : data});
},
This way, render()
will only be called once, and you'll have the data you're looking for in the initial render.
Upvotes: 29
Reputation: 61
In React, props
are used for component parameters not for handling data. There is a separate construct for that called state
. Whenever you update state
the component basically re-renders itself according to the new values.
var BookList = React.createClass({
// Fetches the book list from the server
getBookList: function() {
superagent.get('http://localhost:3100/api/books')
.accept('json')
.end(function(err, res) {
if (err) throw err;
this.setBookListState(res);
});
},
// Custom function we'll use to update the component state
setBookListState: function(books) {
this.setState({
books: books.data
});
},
// React exposes this function to allow you to set the default state
// of your component
getInitialState: function() {
return {
books: []
};
},
// React exposes this function, which you can think of as the
// constructor of your component. Call for your data here.
componentDidMount: function() {
this.getBookList();
},
render: function() {
var books = this.state.books.map(function(book) {
return (
<li key={book.key}>{book.name}</li>
);
});
return (
<div>
<ul>
{books}
</ul>
</div>
);
}
});
Upvotes: 6