React fetch data in server before render
ReactjsReactjs Problem Overview
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"
- getDefaultProps
- return
- fetch
- {data: {books: [{..}, {..}]}}
However, I expect the code should run in this sequence
- getDefaultProps
- fetch
- {data: {books: [{..}, {..}]}}
- return
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>
);
}
Reactjs Solutions
Solution 1 - Reactjs
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.
Solution 2 - Reactjs
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>
);
}
}
Solution 3 - Reactjs
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}/>
}
) }
Solution 4 - Reactjs
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>
);
}
});
Solution 5 - Reactjs
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}));
},
Solution 6 - Reactjs
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>
);
}
}
Solution 7 - Reactjs
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:
Solution 8 - Reactjs
You can use redial package for prefetching data on the server before attempting to render
Solution 9 - Reactjs
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