[英]Flux/Alt data dependency, how to handle elegantly and idiomatically
我正在使用alt作為項目的助焊器實現,並且無法繞過處理兩個相關實體的加載存儲的最佳方式。 我正在使用sources功能和registerAsync來處理我的async / api調用,並使用AltContainer將它們綁定到我的視圖。
我有兩個由conversationId一對一關聯的實體。 兩者都通過api調用加載:
一旦我的作業商店加載了數據,我想填充對話商店。
我使用源來加載作業存儲:
module.exports = {
fetchJobs() {
return {
remote() {
return axios.get('api/platform/jobs');
},....
看起來像waitFor()方法的作業,但似乎在一個商店的內容需要轉換或與另一個商店的內容合並時使用。 我需要根據另一個數據存儲的內容獲取一個數據存儲的內容。
一般來說,我需要:
我天真的解決方案是引用作業存儲中的對話操作,並在數據到達時發送事件。 像這樣的東西:
var jobActions = require('../actions/Jobs');
var conversationActions = require('../actions/Conversations');
class JobStore {
constructor() {
this.bindListeners({
handlefullUpdate: actions.success
});...
}
handlefullUpdate(jobs) {
this.jobs = jobs;
conversationActions.fetch.defer(jobs);
}
}
當然,這樣做違反了商店不應該發送事件的格言,因此我必須使用延遲在調度中間發送一個動作。 這對我來說很有意義,因為它似乎沿着這條路走下去,我在我的代碼中重新引入了各種各樣的副作用; 失去了我應該看到的“功能性管道”之美。
此外,我的工作存儲必須持有對任何依賴實體的引用,以便它可以分派適當的操作。 在這里,我只有一個,但我可以想象很多。 就實體之間的依賴關系而言,這似乎完全倒退。
我想到了幾種選擇:
我可以在源/操作中調用api / platform / jobs端點來獲取所有對話,只是為了獲取id。 最初的方法更有效率,但這似乎更為真實,因為我失去了所有的交談。
我也可以使用單個動作/源同時獲取兩者,返回{jobs:{}, conversations: in the action}
(使用promises編排依賴關系)並使用它來填充兩個商店。 但這種方法對我來說似乎不必要地復雜化(我覺得我不應該這樣做!)。
但我錯過了另一種方式嗎? 看起來奇怪的是,這樣一個常見的用例會破壞磁通天堂的優雅和/或迫使我跳過如此多的箍。
@dougajmcdonald 在這里提出了類似的問題,但也許它的措辭過於普遍,並沒有得到任何牽引力:
這看起來似乎是Flux設計中的一個缺陷,而你是對的,它是一個非常常見的用例。 我一直在研究這個問題(以及一些類似的問題),以便在我自己的系統中更好地實現Flux,雖然肯定有選擇,但大多數都有缺點。
最流行的解決方案似乎是使用容器(例如AltContainer )來抽象從apis請求數據的任務,並從商店加載到單個組件中,沒有ui邏輯。 此Container將負責查看商店中的信息,並確定是否需要執行其他操作才能完成這些信息。 例如;
static getPropsFromStores() {
var data = SomeStore.getState();
if (/*test if data is incomplete */){
SomeAction.fetchAdditionalData();
}
return data;
}
這說得通。 我們的Component層中有邏輯和組件,這是Flux所說的所在。
直到你考慮有兩個安裝容器要求相同信息的可能性(從而復制任何初始化提取,即模型中的對話),如果你添加第三個(或第四個或第五個)容器訪問,問題只會變得更糟那些商店。
對容器人群的反應是“只要不要多於一個!”,這是一個很好的建議......如果你的要求還可以。
所以這是我改變的解決方案,圍繞着相同的概念; 在整個應用程序周圍(或甚至旁邊)包含一個主容器/組件。 與傳統容器不同,此主容器/組件不會將商店屬性傳遞給其子容器。 相反,它完全負責數據的完整性和完整性。
這是一個示例實現;
class JobStore {
constructor() {
this.bindListeners({
handleJobUpdate: jobActions.success
});
},
handleJobUpdate(jobs) {
this.jobs = jobs;
}
}
class ConversationStore {
constructor() {
this.bindListeners({
handleJobUpdate: jobActions.success,
handleConversationUpdate: conversationActions.success
});
},
handleJobUpdate(jobs) {
this.conversations = {};
this.tmpFetchInfo = jobs.conversation_id;
this.isReady = false;
},
handleConversationUpdate(conversations) {
this.conversations = conversations;
this.tmpFetchInfo = '';
this.isReady = true;
}
}
class MasterDataContainer {
static getPropsFromStores() {
var jobData = JobStore.getState();
var conversationData = ConversationStore.getState();
if (!conversationData.isReady){
ConversationAction.fetchConversations(conversationData.tmpFetchInfo);
}
},
render: function(){
return <div></div>;
}
}
到目前為止,我提供的最佳解決方案(以及示例似乎遵循的解決方案)是在作業操作中添加“jobsFetched”操作,並在數據到達時調度它。
var jobActions = require('../actions/Jobs');
var ConversationActions = require('../actions/Conversations');
class JobStore {
constructor() {
this.bindListeners({
handlefullUpdate: jobActions.success...
});...
}
handlefullUpdate(jobs) {
this.jobs = jobs;
ConversationActions.jobsFetched.defer(jobs);
}
}
class ConversationStore {
constructor() {
this.bindListeners({
handleJobUpdate: jobActions.jobsFetched...
});...
}
handleJobUpdate(jobs) {
/*Now kick off some other action like "fetchConversations"
or do the ajax call right here?*/
}
}
這消除了作業存儲必須保持對其所有依賴對象的引用的問題,但是我仍然必須從我的商店內部調用一個動作,並且我必須引入jobsFetched動作來設置ConversationStore以獲取它的數據。 因此,我似乎無法使用源代碼進行對話。
誰能做得更好?
我一直在關注@ gravityplanx的答案,但我不確定它改善了多少情況。
重申並放大:我真的非常喜歡從一個來源加載我的商店的alt模式。 每個需要商店的組件看起來都是這樣的:
export default class extends React.Component {
componentDidMount() {
CurrentUser.fetch();
Jobs.fetch();
};
render() {
return(
<AltContainer stores={{user:CurrentUser, jobs:Jobs}}>
<Body/>
</AltContainer>)
}
}
意圖一目了然是可以理解的。 我可以清楚地分離問題,從而更容易測試我的行為/商店和來源。
但是從我的問題來看,這個美麗的模式在常見的用例中被打破了,我最終不得不在我的行動和商店中創建一些相當復雜的管道來協調ajax調用對話。 另一個答案似乎只是將這種復雜性轉移到別處。 我希望它消失了。
我最后做的是完全分開商店(問題中提出的第一個解決方案)。 我做了一個額外的ajax調用來獲取jobId,而另一個是在JobConversation源中獲取對話。 像這樣的東西:
axios.get(window.taxFyleApi + 'api/platform/active-jobs-pick/layerConversation?jobId=' + jobId)
.then((res)=> {
let job = res.data[0]; //Returns an array with only one item
let qBuilder = window.layer.QueryBuilder.messages().forConversation(job.conversationId)...
我保留了將AltContainer與我的組件一起使用的好方法,並丟失了所有的編排管道。 現在,我認為由此產生的清晰度值得額外的后端呼叫。
我也知道我是怎么想它的工作(在符號而言),並且會問@goatslacker一下,或者可以采取射擊在做我自己。 我希望能夠在商店的exportAsync()方法中指定依賴項。 我很樂意說:
class JobConvesationStore {
constructor() {
this.exportAsync(source, JobStore);
}
在JobStore擁有其數據之前,不會調用異步方法JobConversationStore。 意圖易於閱讀,不需要復雜的動作編排。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.