node.jsreactjsnext.jsreact-universal

How to render data received from a REST service in React Universal? (Next.js)


I want to receive data via a REST service call in my React Universal (with Next.js) app using fetch() and then render the result into JSX like this:

class VideoPage extends Component {
  componentWillMount() {
    console.log('componentWillMount');

    fetch(path, {
      method: 'get',
    })
      .then(response =>
        response.json().then(data => {
          this.setState({
            video: data,
          });
          console.log('received');
        })
      );
  }

  render() {
    console.log('render');
    console.log(this.state);
    if (this.state && this.state.video) {
      return (
        <div>
          {this.state.video.title}
        </div>
      );
    }
  }
}

export default VideoPage;

Unfortunately, the output is this:

componentWillMount
render
null
received

Which does make sense because the call to fetch is asynchronously and render() finishes before the call to the REST service has finished.

In a client-side app this would be no problem because a change of state would call render() which then updates the view, but in a universal app, especially with JavaScript turned off on the client, this is not possible.

How can I solve this?

Is there a way to call the server synchronously or delay render()?


Solution

  • In order to get it working, I had to do 3 things:

    Code looks like this now:

    static async getInitialProps({ req }) {
      const path = 'http://path/to/my/service';
      const res = await fetch(path);
      const json = await res.json();
      return { video: json };
    }
    

    Then, in render() I can access the data via this.props.video, for example:

    render() {
      return (
        <div>{this.props.video.title}</div>
      );
    }