Flux:从 AJAX 向组件返回值

Posted

技术标签:

【中文标题】Flux:从 AJAX 向组件返回值【英文标题】:Flux: return values from AJAX to the component 【发布时间】:2014-12-14 14:37:49 【问题描述】:

我正在开发简单的 Flux+Reactjs 应用程序,在我的商店里有:

var ProfileStore = merge(EventEmitter.prototype, 
/**
   * Get the entire collection of Profiles.
   * @return object
   */
  getAll: function() 
    //ajax call to mongolab
    var url = "https://api.mongolab.com/api/1/databases/bar/collections/profiles?apiKey=foo-"
    var jsPromise = Promise.resolve($.ajax(url));
    jsPromise.then(function(response) 
        return response;
    );

  ,

  emitChange: function() 
    this.emit(CHANGE_EVENT);
  ,

  /**
   * @param function callback
   */
  addChangeListener: function(callback) 
    this.on(CHANGE_EVENT, callback);
  ,

  /**
   * @param function callback
   */
  removeChangeListener: function(callback) 
    this.removeListener(CHANGE_EVENT, callback);
  
);

然后在我的组件中:

var ProfileApp = React.createClass(

    getInitialState: function() 
        return 
            allProfiles: ProfileStore.getAll()
        ;
    ,

);

当我在 getAll() 函数中使用 console.log() 时,我可以看到结果,但没有任何东西传递给我的组件,是否有任何关于如何解决此问题的指针?

【问题讨论】:

在您的商店中进行异步调用并不总是最好的解决方案,因为正如您所注意到的那样,它使您更难推断您的状态发生了什么。我建议阅读以下文章,该文章描述了使用通量处理异步请求的更好方法:code-experience.com/… 【参考方案1】:

其他人指出了您的代码中与 Promise 相关的问题。但是,您尝试在商店中执行的操作的另一个问题是您尝试直接处理响应,而不是调度新操作。

在 Flux 中,数据应始终源自操作。函数getAll() 应该只返回本地存储的allProfiles,类似于 andrewkshim 建议的,但没有 XHR。

相反,将您尝试执行的操作分解为以下类型的两个操作:

PROFILES_REQUEST_GET :这是由用户单击按钮或任何导致发出请求的原因创建的——它会启动 XHR 请求。

PROFILES_REQUEST_SUCCESS :这是由 XHR 的成功回调创建的操作。此操作包含新数据,并且商店可以相应地填充其allProfiles

【讨论】:

上面的代码是您尝试初始化应用程序的方式吗?如果是这样,只需调用引导模块中的数据,然后在成功回调中创建一个操作,类似于我在回答中描述的内容。 感谢您的反馈,您提到在 Flux 中,数据应始终源自操作,在我的情况下,我想立即加载组件呈现的配置文件,而不是响应操作,方法和你上面建议的一样吗? 重写了应用程序并遵循了flux-chat应用程序的代码这里是要点gist.github.com/johnwesonga/316b26760e2be043bbb1现在工作得很好【参考方案2】:

使用 Flux 的设置要求您以不同的方式思考数据流动的方式。您可以做的是设置您的商店,以便首先包含对allProfiles 的引用,即undefined(或null 或您喜欢的任何指定的“空”值),然后在@987654326 时更新@ 被调用:

var allProfiles;  // the reference to your profile data

var ProfileStore = merge(EventEmitter.prototype, 

  getAll: function() 
    var thisStore = this;

    var url = "YOUR MONGO URL"
    var jsPromise = Promise.resolve($.ajax(url));
    jsPromise.then(function(response) 
        allProfiles = response
        this.emitChange();
    );

    return allProfiles;
  

// other methods omitted to save space

我应该指出你在这里使用的承诺是不正确的。 then 函数返回另一个承诺,因此您将 allProfiles 设置为承诺,而不是集合。在上面的示例中,我们返回对配置文件数据的引用,该引用将在 AJAX 调用完成后更新。数据更新后,存储会发出更改,让所有侦听器知道他们也应该更新。

在您的组件中,您可以将其设置为侦听您的ProfileStore 上的CHANGE_EVENT,以便在ProfileStore 的数据更改时更新其状态。但是请注意,一开始,allProfiles 数据将为空,因此我们需要向用户传达我们正在加载数据:

var ProfileApp = React.createClass(

  updateAllProfiles: function () 
    this.setState(
      allProfiles: ProfileStore.getAll()
    );
  ,

  getInitialState: function () 
    return 
      allProfiles: ProfileStore.getAll()
    
  ,

  componentDidMount: function() 
    ProfileStore.addChangeListener(this.updateAllProfiles);
  ,

  componentWillUnmount: function () 
    ProfileStore.removeChangeListener(this.updateAllProfiles)
  ,

  render: function () 
    var profiles;
    if (this.state.allProfiles) 
      // you'll need to figure out how you want to render the profiles
      // the line below will likely not be enough
      profiles = this.state.allProfiles;  
     else 
      profiles = 'Loading...';
    
    return <div id='profiles'>profiles</div>;
  

);

我在 JSFiddle 上做了一个非常人为的例子。由于环境的限制,我不得不想出几个模拟的hack,但我希望你能修改这个例子,更好地了解发生了什么:http://jsfiddle.net/berh6gxs/2/

我还掩盖了很多细节(例如,您可能希望在 ProfileApp 组件上实现 shouldComponentUpdate 方法,以便仅在数据实际更改时重新渲染),但我感觉到了这些细节对回答手头的问题不太重要。我之所以提到这一点,是因为您无法接受我在这里所说的并立即想出一个完美的解决方案。

我还建议研究 Flux 的第三方实现。雅虎提出了一个非常好的:https://github.com/yahoo/flux-examples

黑客愉快!

【讨论】:

【参考方案3】:

这段代码:

jsPromise.then(function(response) 
    return response;
);

没有做我认为你认为的事情。它等待jsPromise 解析,并使用响应调用回调,并且从回调返回的任何内容都将成为从jsPromise.then 返回的new promise 的已解析值。查看 this javascript Promises guide 了解有关 Promise 工作原理的更多信息。

至于您的问题:简而言之,您不能从异步操作中返回值;你总是不得不求助于回调。例如:

var ProfileStore = merge(EventEmitter.prototype, 
  // getAll returns a Promise that resolves to all the profiles
  getAll: function() 
    var url = "..."
    return Promise.resolve($.ajax(url));
  ,
  // ...
);

var ProfileApp = React.createClass(
  getInitialState: function() 
    return 
      allProfiles: [] // initially we have no profiles
                      // could also have some "loading" sentinel, etc
    ;
  ,

  componentDidMount: function() 
    // Then we ask for all the profiles and *asynchronously*
    // set the state so the component updates.
    ProfileStore.getAll().then(function(response) 
      this.setState(allProfiles: response);
    .bind(this));
  
);

【讨论】:

本例中,store如何知道ajax调用(响应对象)返回的数据? @nilgun 我不太明白你的问题,但上面fisherwebdev的回答可能会有所帮助

以上是关于Flux:从 AJAX 向组件返回值的主要内容,如果未能解决你的问题,请参考以下文章

Flux 完成时的返回值?

我无法在 C# 中使用 Jquery 和 Ajax 从控制器向 Razor 视图发送值

React+Flux:通知视图/组件操作失败?

asp.net利用Ajax和Jquery在前台向后台传参数并返回值

React + Flux:动作可以返回值吗? (例如最后创建的 id)

asp.net 利用Ajax和Jquery在前台向后台传参数并返回值