2014-10-19 74 views
3

我的工作簡單的流量+ 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()時,我可以看到結果,但沒有任何內容被傳遞給我的組件,關於如何修復此問題的指針?

+0

從您的店內進行異步呼叫並不總是最好的解決方案,因爲它會讓您更難以推斷您的狀態正在發生什麼,正如您已經注意到的那樣。我建議閱讀以下文章,其中描述了更好的方式來處理與flux的異步請求:http://www.code-experience.com/async-requests-with-react-js-and-flux-revisited/ – 2014-10-19 18:13:18

回答

3

其他人已經指出了你的代碼中與promise有關的問題。但是,您在商店中試圖執行的另一個問題是您嘗試直接處理響應,而不是分派新的操作。

在Flux中,數據應始終始於一個操作。函數getAll()應該只返回本地存儲的allProfiles,類似於andrewkshim建議的內容,但不包含XHR。

相反,打破你正在試圖做成兩個動作有以下類型的內容:

PROFILES_REQUEST_GET:這是由用戶點擊按鈕創建,或任何導致被提出的要求 - 它啓動XHR請求。

PROFILES_REQUEST_SUCCESS:這是XHR成功回調創建的操作。此操作包含新數據,並且商店可以相應地填充其allProfiles

+0

上面的代碼是如何嘗試初始化你的應用的?如果是這樣,只需調用bootstrap模塊中的數據,然後在成功回調中創建一個動作,就像我在回答中所描述的那樣。 – fisherwebdev 2014-10-20 06:23:13

+0

感謝您的反饋意見,您提到在Flux中,數據應始終始於動作,在我的情況下,我希望立即加載組件呈現的配置文件,而不是響應某個動作,這種方法是否與您的操作相同?上面提出的建議? – jwesonga 2014-10-20 11:41:10

+0

重寫了應用程序,並遵循flux-chat應用程序的代碼,這裏是要點https://gist.github.com/johnwesonga/316b26760e2be043bbb1現在工作得很好 – jwesonga 2014-10-27 18:35:16

1

此代碼:

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

沒有做什麼,我想你想它。它等待jsPromise解決,使用響應調用回調,並且從回調中返回的任何內容都將成爲從jsPromise.then返回的新的承諾的解析值。查看this JavaScript Promises guide瞭解承諾如何工作的更多信息。

至於你的問題:簡而言之,你不能從異步操作返回一個值;你將永遠不得不求助於回撥。例如:

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

在這個例子中,商店如何知道ajax調用(響應對象)返回的數據? – nilgun 2016-11-16 23:19:56

+0

@nilgun我不太明白你的問題,但上面的fisherwebdev的回答可能有幫助 – 2016-11-17 11:30:19

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函數返回另一個承諾,因此您將allProfiles設置爲承諾而不是集合。在上面的示例中,我們將返回對配置文件數據的引用,該配置文件將在AJAX調用完成後更新。一旦數據被更新,商店發出一個改變,讓所有的聽衆知道他們也應該更新。

在你的組件,你可以設置它,所以它聽CHANGE_EVENTProfileStore,使得更新其狀態每當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/

我也掩蓋了很多(例如,您可能希望在您的ProfileApp組件上實施shouldComponentUpdate方法,以便在數據實際更改時僅重新呈現),但我覺得這些類型的細節對於回答手邊的問題並不那麼重要。我提到這一點是因爲你無法接受我在這裏所說的話,並立即想出一個完美的解決方案。

我也建議尋找Flux的第三方實現。雅虎已經拿出了一個很不錯的:https://github.com/yahoo/flux-examples

快樂黑客!