有效地构造具有多个视图的大型React.js应用程序

时间:2015-04-15 11:40:57

标签: javascript reactjs reactjs-flux

我有一个包含多个视图的反应应用程序(例如ProfileFollowers)。这些视图中的每一个当前都是它自己的React组件,它在服务器端和客户端都执行。我开始努力理解构造代码的正确方法。

例如,假设已加载Profile并且用户点击“查看关注者”。此时,将向调度程序发送一条消息gotoFollowers。在顶层,我们然后渲染一个全新的React组件(即Followers组件)。不幸的是,FollowerProfile组件都需要类似的数据(例如会话数据,用户照片,用户名,甚至是关注者,因为我们在Profile上显示了一小部分关注者)。因此,每次用户更改页面时,必须在顶层重新渲染(包括执行ajax调用)是非常浪费的。

我认为我没有正确地做到这一点。我应该使用单亲React组件吗? (我也看到了问题)我应该如何构建具有许多视图的大型React应用程序?

window.render = function (page, id, pushState) {
  var stateURL = '';

  switch (page) {
    case 'Profile':
      stateURL = '/' + id;
      async.parallel({
        profileData: function (callback) { MemberLoader.load(id, callback); },
        followerData: function (callback) { FollowerLoader.load(id, callback); },
        },
        sessionData: function (callback) { Session.get(callback); },
      }, function (err, results) {
        var component = React.createFactory(Profile);
        React.render(component({ member: results.profileData, followers: results.followerData, session: results.sessionData }), mountNode);
      });
      break;
    case 'Followers':
      stateURL = '/' + id + '/followers';
      async.parallel({
        profileData: function (callback) { MemberLoader.load(id, callback); },
        sessionData: function (callback) { Session.get(callback); },
        followerData: function (callback) { FollowerLoader.load(id, callback); },
      }, function (err, results) {
        var component = React.createFactory(Followers);
        React.render(component({ member: results.profileData, followers: results.followerData, session: results.sessionData  }), mountNode);
      });
      break;
  };

  if (pushState) {
    window.history.pushState({ page: page, id: id }, null, stateURL);
  } else {
    window.history.replaceState({ page: page, id: id }, null, stateURL);
  }

};

Dispatcher.register(function(event) {
  if (event.action === 'gotoProfile') {
    window.render('Profile', event.username, true);
  } 
  else if (event.action === 'gotoFollowers') {
    window.render('Followers', event.username, false); 
  }
});

注意:我们的应用程序在服务器端和客户端都呈现。

1 个答案:

答案 0 :(得分:0)

要解决多次获取相同数据的问题,您应该查看Promises - 这可能会替换您正在使用的async库。

Promises允许您进行一次异步调用,但即使在成功调用之后也会附加回调。您可以保留对承诺的引用,并在以后的应用程序中使用它 - 而无需检查承诺是否已经解决。

我在下面为你准备了一个(未经测试的)示例实现 - 它做了几件事。

  1. 为每个数据存储创建对promises的引用
  2. 创建一个返回的方法,如果尚未进行异步调用,则返回该承诺
  3. 将您的async.parallel代码替换为Promise.all,这会创建一个新的承诺,在成功解决所有已通过的承诺后解析。
  4. React.render置于对then的承诺中 - 只有在所有异步调用完成后才会调用它。
  5. 见下文:

    var profilePromise;
    var followerPromise;
    var sessionPromise;    
    
    var getProfilePromise = function(){
      //If the profile promise already exists (ie, the async call has already been started)
      //then we return the promise, otherwise we make the async call.
      if(!profilePromise){
        profilePromise = new Promise(function(resolve, reject){
          MemberLoader.load(id, function(){
            //Depending on the result you either call `resolve` and pass
            //in the data, or call `reject` with the error
          });
        });    
      }    
    
      return profilePromise;
    };
    //Repeat a version of the above function for your follower and session promise    
    
    window.render = function (page, id, pushState) {
      var stateURL = '';    
    
      switch (page) {
        case 'Profile':
          stateURL = '/' + id;
          Promise.all([
            getProfilePromise(),
            getFollowerPromise(),
            getSessionPromise()
          ]).then(function(results){
            var component = React.createFactory(Profile);
            React.render(component({ member: results[0], followers: results[1], session: results[2] }), mountNode);
          }).catch(function(err){
            /** handle errors here */
          });
          break;
        case 'Followers':
          stateURL = '/' + id + '/followers';
          Promise.all([
            getProfilePromise(),
            getFollowerPromise(),
            getSessionPromise()
          ]).then(function(results) {
            var component = React.createFactory(Followers);
            React.render(component({ member: results[0], followers: results[1], session: results[2]  }), mountNode);
          }).catch(function(err){
            /** handle errors here */
          });
          break;
      }    
    
      if (pushState) {
        window.history.pushState({ page: page, id: id }, null, stateURL);
      } else {
        window.history.replaceState({ page: page, id: id }, null, stateURL);
      }    
    
    };