React fetch data in server before render

Reactjs

Reactjs 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"

  1. getDefaultProps
  2. return
  3. fetch
  4. {data: {books: [{..}, {..}]}}

However, I expect the code should run in this sequence

  1. getDefaultProps
  2. fetch
  3. {data: {books: [{..}, {..}]}}
  4. 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:

https://medium.com/@navgarcha7891/react-server-side-rendering-with-simple-redux-store-hydration-9f77ab66900a

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
},

More on this here

If you are using hooks, use the useEffect hook:

useEffect(() => { 
    // Your code goes here
});

Documentation on useEffect

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfingercrossView Question on Stackoverflow
Solution 1 - ReactjsMichael ParkerView Answer on Stackoverflow
Solution 2 - ReactjsMahdi BashirpourView Answer on Stackoverflow
Solution 3 - ReactjsAsmaa AlmadhounView Answer on Stackoverflow
Solution 4 - ReactjshkalView Answer on Stackoverflow
Solution 5 - ReactjsHughView Answer on Stackoverflow
Solution 6 - ReactjsRostislav MatlView Answer on Stackoverflow
Solution 7 - ReactjsnavView Answer on Stackoverflow
Solution 8 - ReactjsSergey AndreevView Answer on Stackoverflow
Solution 9 - ReactjsИлья АпролView Answer on Stackoverflow