23

我正在为同构 JavaScript 应用程序做一个 POC,以从服务器端呈现 HTML。POC 使用简单的 HTML,但我想进行 API 调用并获取 JSON 响应并发送到渲染函数。我尝试了各种方法,但它不起作用。

我错过了什么?我对 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>
        )
      }

  });
4

3 回答 3

5

componentWillMount在需要渲染服务器端的情况下,使用获取组件内部不是一个正确的位置。您需要以某种方式将其移出表单组件,并在获取后将实际数据作为道具传递 - 例如@JakeSendar 在他的回答中建议。

我有一些使用 React 做同构应用程序的经验,我面临的主要问题是如何等到所有数据都加载完毕后再进行首次渲染

正如@FakeRainBrigand 在评论中已经提到的那样,不仅有一种方法可以做到这一点,而且取决于您的要求。

构建同构应用程序的方法很少,从我的角度来看,一些有趣的是:https ://github.com/webpack/react-starter和http://fluxible.io/

但是,按照我自己的想法,最优雅的方法是为 React 组件组织异步渲染,尤其是使用 RxJS。

一般来说,我的应用程序结构如下:

  1. 视图 - 没有任何逻辑的反应组件(只是一个视图)
  2. models - 具有当前状态的 Observables(初始数据使用 superagent 加载,然后与其他模型和/或操作结果相结合)。在简单的情况下,它类似于:

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

  3. 动作(或意图) - 用于收集用户输入、做某事并将动作结果发送给订阅者模型和/或其他动作的观察者。在简单的情况下,例如:

    updatesSubject = new Rx.Subject();

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

  4. components - Observables(虚拟 DOM 元素流)由模型、其他组件和操作组合而成(我对此有一个注释,解释了如何以及为什么使用 RxJS 创建 Observable React 元素),现在我也计划添加部分组件(元组来自:反应组件、可观察对象、观察者和属性。部分填充使用 DI)

  5. 路由器 - 负责处理位置变化的组件,通常主要功能是将位置变化映射到虚拟 DOM 元素和元信息流。但在细节上,在我的情况下它有点复杂(url生成,活动url突出显示,导航时处理滚动,还有嵌套路由和多个视图的可能性)

所有这些都是使用 DI 容器组装在一起的,在我的情况下类似于angular2 DI 容器,但为了我的特定需求而进行了很多简化。

组件、模型和动作是使用 DI 创建的。

在服务器端应用程序是这样的:

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)
          });
       });
}

和客户端类似:

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)
   });

与flux相比,它是组织应用程序的更具声明性和更强大的方式。在同构应用程序的情况下 - 对我来说,各种带有通量的黑客看起来要好得多。但当然有缺点...... - 它更复杂。

可能稍后,我将开源所有这些,但现在 - 它还没有准备好发布。

UPD1:

原始答案有点过时(稍后我打算更新它),我在这方面取得了一些进展。

上述代码的链接,已经开源:

  • DI 容器:di1
  • 反应组件的容器(将视图连接到可观察对象和观察者):rx-react-container
  • Starter 模板,用于实现同构小部件,使用 RxJS 和 React,以及上面的库:Reactive Widgets

关于完整的应用程序(工作仍在进行中,文档不太好,但总的来说应该很清楚):

于 2015-04-29T11:51:20.553 回答
3

React 的renderToString方法(用于在服务器上渲染组件)是同步的。因此,任何类型的异步任务,例如您的 api 请求,在组件呈现时仍将处于挂起状态。

有几种方法可以解决此问题,具体取决于您是否要在服务器或客户端上获取数据。

如果您选择在服务器上获取数据,请首先将您的 api-request 逻辑移到组件之外。然后,在回调中渲染您的组件,将获取的数据作为prop. 它看起来像这样:

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

在您的组件内部,您可以通过this.props.categories.

另一种选择是在客户端处理 api 请求。您将在 中发出 AJAX 请求componentDidMount,并根据获取的数据设置组件的状态。它看起来与您现在所拥有的非常相似,主要区别在于您的请求逻辑将存在于componentDidMount(异步,在客户端上调用)而不是componentWillMount(不是异步,在服务器上调用)。

于 2015-04-29T08:47:58.903 回答
0

你应该使用superagent,对我来说真的很好,你也错过了最重要的部分,你应该使用flux从服务器获取数据,flux是facebook强烈推荐的方式,它很容易使用flux architecture

于 2015-06-09T06:49:49.033 回答