【问题标题】:Flux: return values from AJAX to the componentFlux:从 AJAX 向组件返回值
【发布时间】: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/…

标签: reactjs reactjs-flux


【解决方案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的回答可能会有所帮助
    猜你喜欢
    • 2016-11-04
    • 2019-06-15
    • 2016-04-13
    • 2016-08-23
    • 1970-01-01
    • 2018-04-16
    • 2021-07-30
    • 2022-01-23
    • 2020-10-08
    相关资源
    最近更新 更多