3

我正在开发简单的 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() 时,我可以看到结果,但是没有任何东西被传递给我的组件,关于如何解决这个问题的任何指针?

4

3 回答 3

3

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

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

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

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

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

于 2014-10-19T22:49:45.823 回答
2

Flux 的设置要求您以不同的方式思考数据流动的方式。您可以做的是设置您的商店,以便首先保存对 的引用allProfiles,该引用是undefined(或null您希望的任何指定的“空”值),然后在getAll()调用时更新:

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函数返回另一个promise,因此您设置allProfiles的是promise,而不是集合。在上面的示例中,我们返回对配置文件数据的引用,该引用将在 AJAX 调用完成后更新。更新数据后,存储会发出更改以让所有侦听器都知道他们也应该更新。

在您的组件中,您可以对其进行设置,以便它CHANGE_EVENT在您的设备上监听,以便在' 的数据更改ProfileStore时更新其状态。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 上做了一个非常人为的例子。由于环境的限制,我不得不想出几个模拟的黑客,但我希望你能修改这个例子并更好地了解正在发生的事情:http: //jsfiddle.net/berh6gxs/2 /

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

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

快乐黑客!

于 2014-10-19T18:28:46.337 回答
1

这段代码:

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

没有做我认为你认为的事情。它等待jsPromise解析,使用响应调用回调,并且从回调返回的任何内容都将成为jsPromise.then. 查看此 JavaScript Promises 指南,了解有关 Promises 如何工作的更多信息。

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

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));
  }
});
于 2014-10-19T18:07:38.717 回答