2016-02-11 5 views
9

Używam alt jako mojej implementacji strumienia dla projektu i mam problem z zawijaniem głowy wokół najlepszego sposobu obsługi magazynów ładujących dla dwóch powiązanych obiektów. Używam funkcji sources wraz z registerAsync do obsługi moich połączeń async/api i wiązania ich z moimi widokami za pomocą AltContainer.Zależność danych Flux/Alt, jak obsługiwać elegancko i idiomatycznie

Mam dwie jednostki powiązane jeden do jednego przez identyfikator conversationl. Oba są ładowane za pomocą wywołania API:

enter image description here

Raz mój sklep praca jest ładowany z danych Chcę wypełnić sklep rozmowy.

używam źródło załadować sklep praca:

module.exports = { 
    fetchJobs() { 
     return { 
      remote() { 
       return axios.get('api/platform/jobs'); 

      },.... 

Wygląda na to zadanie dla waitfor() metody, ale wydaje się do użycia, gdy zawartość jednego sklepu wymagają przekształcenia lub połączenia z zawartością innej. Potrzebuję pobrać zawartość jednego magazynu danych na podstawie zawartości innej.

Ogólnie muszę:

  • Zaproszenie strona trzecia API i załadować listę podmiotów do sklepu.
  • Kiedy te dane się pojawiają, muszę użyć atrybutu z każdego z powyższych, aby wywołać inny interfejs API i załadować te dane do innego sklepu.

Moje naiwne rozwiązanie polega na przywoływaniu akcji konwersacji ze sklepu i wysyłaniu zdarzeń w momencie nadejścia danych. Coś takiego:

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

Oczywiście, ten sposób narusza dictum, że sklepy nie powinny wywołują zdarzenia, a więc muszę używać odraczać wysyłką akcję w środku wysyłki. To ma dla mnie sens, ponieważ wydaje się, że idąc tą ścieżką, przywracam różnego rodzaju efekty uboczne w moim kodzie; tracąc piękno "funkcjonalnych rurociągów", które powinienem widzieć z płynnością.

Ponadto, mój sklep musi zawierać odniesienia do wszelkich podmiotów zależnych, aby mógł wysłać odpowiednią akcję. Tutaj mam tylko jedną, ale mogę sobie wyobrazić wiele. Pod względem zależności między podmiotami wydaje się to całkowicie wstecz.

Kilka alternatywnych przychodzą na myśl:

mogę nazwać API/platform/Praca końcowy w źródle/działania gdzie mogę pobrać wszystkie rozmowy, tak aby uzyskać identyfikator. Oryginalne podejście jest bardziej efektywne, ale wydaje się, że jest to bardziej prawdziwe, ponieważ tracę całą rozmowę.

Mogę też mieć jedną akcję/źródło, które pobiera oba, zwracając {jobs:{}, conversations: in the action} (organizując zależność tam przy użyciu obietnic) i wykorzystuję to zapełnianie obu sklepów. Ale to podejście wydaje mi się niepotrzebnie skomplikowane (wydaje mi się, że nie powinienem tego robić!).

Ale czy brakuje mi innej drogi? Wydaje się dziwne, że taki powszechny przypadek użycia przełamie elegancję paradimu strumienia i/lub zmusi mnie do przeskoczenia przez tak wiele obręczy.

@dougajmcdonald postawione podobne pytanie here, ale może to było sformułowane zbyt ogólnie, a nie dostał żadnej przyczepności:

Odpowiedz

0

Szukałem odpowiedzi @ gravityplanx, ale nie jestem pewien, jak bardzo poprawia sytuację.

Powtórzyć i wzmocnić: Naprawdę bardzo podoba mi się wzór alt ładowania moich sklepów ze źródła. Każdy komponent, który potrzebuje sklepu (ów) wygląda tak:

export default class extends React.Component { 
    componentDidMount() { 
     CurrentUser.fetch(); 
     Jobs.fetch(); 
    }; 
    render() {  
     return(
      <AltContainer stores={{user:CurrentUser, jobs:Jobs}}> 
       <Body/> 
      </AltContainer>) 
    } 

} 

Intencja jest zrozumiała na pierwszy rzut oka. Dostaję czysty rozdział obaw, co ułatwia testowanie moich działań/sklepów i źródeł.

Ale piękny wzór załamuje się w typowym przypadku użycia z mojego pytania, a ja kończy się konieczności stworzenia dość skomplikowanej hydrauliki w moich działaniach i sklepach do aranżacji wywołań ajax dla rozmów. Druga odpowiedź wydaje się po prostu przenieść tę złożoność w inne miejsce. Chcę, żeby to minęło.

To, co zrobiłem, to całkowicie oddzielić sklepy (pierwsze rozwiązanie sugerowane w pytaniu). Tworzę dodatkowe wywołanie ajaxowe, aby pobrać rozmowęId z pracy, a drugie, aby pobrać rozmowy ze źródła JobConversation. Coś takiego:

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)... 

I zachować piękny sposób korzystania AltContainer z moich komponentów i stracić całą hydraulikę orkiestracji. W tej chwili myślę, że wynikowa klarowność jest warta dodatkowego back-call.

Zdaję sobie również sprawę, że będę działać (pod względem zapisu) i będę pytał o @goatslacker, lub może zrobić zdjęcie samemu. Chciałbym móc określić zależność w metodzie exportAsync() w sklepie. Chciałbym móc powiedzieć: metoda

class JobConvesationStore { 
    constructor() { 
     this.exportAsync(source, JobStore); 
    } 

asynchroniczny JobConversationStore nie będzie nazywany aż JobStore miał swoje dane. Intencja jest łatwa do odczytania i nie będzie potrzebna skomplikowana choreografia.

0

najlepszym rozwiązaniem Doszedłem zz tak daleko (i jeden, że przykłady zdaje się, że następuje) jest dodanie akcji "jobsFetched" do moich działań na stanowiska i wysłanie go, gdy dane nadejdą.

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?*/ 

    } 

} 

Eliminuje to problem przechowywanie zadań mającego posiadać odniesienie do wszystkich zależnych swoich obiektów, ale wciąż mam zadzwonić działanie od wewnątrz mojego sklepu, a ja muszę przedstawić jobsFetched działań, które konfiguruje ConversationStore, aby pobrać dane. Wygląda więc na to, że nie mogę użyć źródła do moich rozmów.

Czy ktoś może zrobić lepiej?

1

To zdecydowanie wydaje się być wadą w projekcie Flux, i masz rację, jest to bardzo powszechny przypadek użycia. Badałem ten problem (i kilka podobnych) przez kilka dni, aby lepiej zaimplementować Flux w moim własnym systemie, i chociaż istnieją zdecydowanie opcje tam, większość ma wady.

Najpopularniejszym rozwiązaniem wydaje się być użycie kontenerów, takich jak AltContainer, aby odciąć zadanie żądania danych od API i ładowanie ze sklepów do pojedynczego komponentu, nieobecnego w logice. Ten kontener byłby odpowiedzialny za wyświetlanie informacji z magazynów i ustalenie, czy wymagane są dodatkowe działania, aby je wypełnić. Na przykład;

static getPropsFromStores() { 
    var data = SomeStore.getState(); 
    if (/*test if data is incomplete */){ 
     SomeAction.fetchAdditionalData(); 
    } 
    return data; 
} 

Ma to sens. Mamy logikę i komponent w naszej warstwie Component, gdzie Flux mówi, że należy.

Do czasu rozważenia możliwości zainstalowania dwóch zamontowanych pojemników z prośbą o podanie tych samych informacji (i tym samym powielenia wszelkich pobranych inicjalizacji, tj. Rozmów w modelu), a problem jest tylko gorszy, jeśli doda się jedną trzecią (lub czwartą, piąty) kontener uzyskujący dostęp do tych samych sklepów.

Konserwy odpowiedzi na to z tłumu Container to "Po prostu nie więcej niż jeden!", Co jest świetną radą ... jeśli Twoje wymagania są w porządku.

Oto moje zmienione rozwiązanie, wokół tej samej koncepcji; Dołącz jeden główny kontener/komponent wokół całej aplikacji (lub nawet obok niej). W przeciwieństwie do tradycyjnych pojemników, ten pojemnik/komponent główny będzie , a nie przekazywać właściwości magazynu do jego elementów podrzędnych. Zamiast tego jest ona odpowiedzialna za integralność i zakończenie danych.

Oto przykładowa implementacja;

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

Sprawdzę twój kod, kiedy moja głowa będzie wyraźniejsza. Dzięki. –

+0

Zrobiłem ci prezent. Brak sensu w punktach odchodzących. –

Powiązane problemy