admin管理员组

文章数量:1202334

I am doing a POC for isomorphic JavaScript application to render HTML from the server side. The POC is working with simple HTML, but I want to make an API call and get the JSON response and send to the render function. I tried various ways but it is not working.

What am I missing? I am very new to React.js.

loadCategoriesFromServer: function() {
        var self = this;

// get walking directions from central park to the empire state building
    var http = require("http");
    url = "api url here";
        var request = http.get(url, function (response) {
            // data is streamed in chunks from the server
            // so we have to handle the "data" event    
            var buffer = "", 
                data,
                route;

            response.on("data", function (chunk) {
                buffer += chunk;
            }); 

            response.on("end", function (err) {
                
              data = JSON.parse(buffer);
                
              //console.log(data.d);
              //console.log(data.d.Items);
                self.setState({
                    categories: data.d.Items
                });
            }); 
        });
    }, // load from server end

    getInitialState: function() {
        return { categories: [] };
    },

    componentWillMount: function() {
        console.log("calling load categories")
        this.loadCategoriesFromServer();
    },
render: function () {
        
        //console.log("data");
        //console.log(this.state.categories);
      
        var postNodes = this.state.categories.map(function (cat) {
          console.log(cat);
        });
    
        return (
          <div id="table-area">
             //i want to paint the data here..
          </div>
        )
      }

  });

I am doing a POC for isomorphic JavaScript application to render HTML from the server side. The POC is working with simple HTML, but I want to make an API call and get the JSON response and send to the render function. I tried various ways but it is not working.

What am I missing? I am very new to React.js.

loadCategoriesFromServer: function() {
        var self = this;

// get walking directions from central park to the empire state building
    var http = require("http");
    url = "api url here";
        var request = http.get(url, function (response) {
            // data is streamed in chunks from the server
            // so we have to handle the "data" event    
            var buffer = "", 
                data,
                route;

            response.on("data", function (chunk) {
                buffer += chunk;
            }); 

            response.on("end", function (err) {
                
              data = JSON.parse(buffer);
                
              //console.log(data.d);
              //console.log(data.d.Items);
                self.setState({
                    categories: data.d.Items
                });
            }); 
        });
    }, // load from server end

    getInitialState: function() {
        return { categories: [] };
    },

    componentWillMount: function() {
        console.log("calling load categories")
        this.loadCategoriesFromServer();
    },
render: function () {
        
        //console.log("data");
        //console.log(this.state.categories);
      
        var postNodes = this.state.categories.map(function (cat) {
          console.log(cat);
        });
    
        return (
          <div id="table-area">
             //i want to paint the data here..
          </div>
        )
      }

  });
Share Improve this question edited Aug 22, 2021 at 9:10 halfer 20.4k19 gold badges108 silver badges201 bronze badges asked Apr 29, 2015 at 6:23 kobekobe 15.8k15 gold badges65 silver badges93 bronze badges 3
  • 2 The isomorphic part is complicated... there's no one right way to do this, and the right way for you depends on a lot of variables from the router you use, to the way your server is organized, to the ways in which you need to fetch data, to the level of abstraction you're comfortable with. – Brigand Commented Apr 29, 2015 at 6:58
  • @FakeRainBrigand do you have any simple example? all i need to make a simple api and wait for the response and pain it thats it. I did something but render is not waiting till the call is done – kobe Commented Apr 29, 2015 at 7:22
  • @FakeRainBrigand, basically i am following the below example. github.com/DavidWells/isomorphic-react-example, only difference is i want the dynamic data from api call. – kobe Commented Apr 29, 2015 at 7:27
Add a comment  | 

3 Answers 3

Reset to default 5

Fetching inside of component using componentWillMount is not a right place, in case when you need to render server side. You need to somehow move it out form component, and pass actual data as props after it is fetched - for example as @JakeSendar suggested in his answer.

I have some experience doing isomorphic app with React, and the main problem I faced is how to wait until all data would be loaded before first render

As @FakeRainBrigand already mentioned in comments, there is not only one way to do this, and it depends from your requirements.

There is few ways to do build an isomorphic app, the some interesting from my perspective is: https://github.com/webpack/react-starter and http://fluxible.io/

But, the most elegant way to do this, as I figured out for myself - is to organise asynchronous rendering for react components, in particular using RxJS.

In general my application is structured as following:

  1. views - React components without any logic (just a view)
  2. models - Observables with current state (initial data is loaded using superagent, then combined with other models and/or actions results). In simple case it is something like:

    Rx.Observable.defer(fetchData).concat(updatesSubject).shareReplay()

  3. actions(or intents) - Observers used to collects user input, do something, and dispatch action results to subscribers models and/or other actions. In simple case something like:

    updatesSubject = new Rx.Subject();

    action = new Rx.Subject(); action.switchMap(asyncRequest).subscribe(updatesSubject)

  4. components - Observables(stream of virtual DOM elements) combined from models, other components and actions (I have a note about this, explaining how and why to create Observable React elements with RxJS), also now I am planning to add partial components (tuple from: react component, observables, observers, and properties. partially filled with using DI)

  5. router - component responsible to handling location changes, in general main feature is to map location changes to stream of virtual DOM elements and meta information. But in details, it is bit more complicated in my case(url generation, active url highlighting, handling scrolls when navigating, also it has possibility of nested routes and multiple views)

All this is assembled together using DI container, in my case similar to angular2 DI container, but a lot simplified for my specific needs.

Components, models and actions are created using DI.

On server side application is like this:

var rootInjector = new Injector();
// setup server specific providers
rootInjector.provide(..., ...)

app.get('/*', function(req,res){
    var injector = rootInjector.createChild();
    // setup request specific providers
    injector.provide(..., ...);

    injector.get(Router)
       .first()
       .subscribe(function(routingResult){ 
          res.render('app', {
              title: routingResult.title,
              content: React.renderToString(routingResult.content)
          });
       });
}

and similar on client side:

var rootInjector = new Injector();
// setup server specific providers
// actually this is omitted in my case because default providers are client side
rootInjector.provide(..., ...)
contentElement = document.getElementById('#content');

rootInjector.get(Router)
   .subscribe(function(routingResult){ 
      document.title = routingResult.title;
      React.render(routingResult.content, contentElement)
   });

In comparison to flux, it is more declarative and more powerful way to organise app. And in case of isomorphic app - for me, it looks much better that various hacks with flux. But of course there is drawbacks... - it is more complicated.

Likely later, I will opensource all this, but for now - it is not quite ready to be published.

UPD1:

Original answer is a bit outdated(later I plan to update it), and I have some progress in this area.

Links to code mentioned above, already opensourced:

  • DI container: di1
  • Container for react componentns(connecting view to observables and obsrvers): rx-react-container
  • Starter template, for implementing isomorphic widgets, using RxJS and React, and libraries above: Reactive Widgets

About complete application(work still in progress, and documentation there is not quite good, but in general it should be clear):

  • Router built especially for isomophic reactive applications router1 and react components to use it router1-react
  • Application template with router and all libraries mentioned above: router1-app-template

React's renderToString method (for rendering components on the server) is synchronous. Therefore, any sort of async task, such as your api request, will still be pending by the time the component has rendered.

There are a couple of ways you can go about fixing this, depending on whether or not you want to fetch your data on the server or client.

If you choose to fetch the data on the server, first move your api-request logic outside of your component. Then, render your component in the callback, passing the fetched-data as a prop. It would look something like this:

response.on("end", function (err) {
  var data = JSON.parse(buffer);
  var markup = React.renderToString(Component({categories: data}));
});

Inside your component, you'd be able to access the data via this.props.categories.

The other option is to handle the api request on the client. You would make an AJAX request in componentDidMount, and set the component's state from the fetched data. It would look very similar to what you have now, the key difference being that your request logic would live in componentDidMount (async, called on the client) rather than componentWillMount (not async, called on the server).

You should use superagent, works really good for me, also you are missing the most important part, you should use flux to fetch data from a server, flux is the way that facebook strongly recommended, it's pretty easy to use flux architecture.

本文标签: javascriptMaking RESTful API call from ReactjsStack Overflow