2014-09-16 13 views
13

Dystrybutor Fluks Facebooka explicitly prohibits ActionCreators from dispatching other ActionCreators. To zastrzeżenie jest prawdopodobnie dobrym pomysłem, ponieważ zapobiega tworzeniu przez aplikację łańcuchów zdarzeń.Unikanie łańcuchów zdarzeń z asynchronicznymi zależnościami danych

To jednak staje się problemem, gdy tylko masz Sklepy zawierające dane z asynchronicznych ActionCreatorów zależnych od siebie. Jeśli CategoryProductsStore zależy od CategoryStore, nie wydaje się, aby można było uniknąć łańcuchów zdarzeń, nie odwołując się do dalszych działań.

Scenariusz 1: Sklep zawierający listę produktów w kategorii musi wiedzieć, z jakiej kategorii należy pobrać identyfikator produktów.

var CategoryProductActions = { 
    get: function(categoryId) { 
    Dispatcher.handleViewAction({ 
     type: ActionTypes.LOAD_CATEGORY_PRODUCTS, 
     categoryId: categoryId 
    }) 

    ProductAPIUtils 
     .getByCategoryId(categoryId) 
     .then(CategoryProductActions.getComplete) 
    }, 

    getComplete: function(products) { 
    Dispatcher.handleServerAction({ 
     type: ActionTypes.LOAD_CATEGORY_PRODUCTS_COMPLETE, 
     products: products 
    }) 
    } 
} 

CategoryStore.dispatchToken = Dispatcher.register(function(payload) { 
    var action = payload.action 

    switch (action.type) { 
    case ActionTypes.LOAD_CATEGORIES_COMPLETE: 
     var category = action.categories[0] 

     // Attempt to asynchronously fetch products in the given category, this causes an invariant to be thrown. 
     CategoryProductActions.get(category.id) 

     ... 

Scenariusz 2: Inny scenariusz jest, gdy składnik dziecko jest zamontowany w wyniku zmiany Store i jego componentWillMount/componentWillReceivePropsattempts to fetch data via an asynchronous ActionCreator:

var Categories = React.createClass({ 
    componentWillMount() { 
    CategoryStore.addChangeListener(this.onStoreChange) 
    }, 

    onStoreChange: function() { 
    this.setState({ 
     category: CategoryStore.getCurrent() 
    }) 
    }, 

    render: function() { 
    var category = this.state.category 

    if (category) { 
     var products = <CategoryProducts categoryId={category.id} /> 
    } 

    return (
     <div> 
     {products} 
     </div> 
    ) 
    } 
}) 

var CategoryProducts = React.createClass({ 
    componentWillMount: function() { 
    if (!CategoryProductStore.contains(this.props.categoryId)) { 
     // Attempt to asynchronously fetch products in the given category, this causes an invariant to be thrown. 
     CategoryProductActions.get(this.props.categoryId) 
    } 
    } 
}) 

Czy istnieją sposoby, aby tego uniknąć bez uciekając się odroczyć?

+0

Dla scenariusza nr 1, umieściłem tego rodzaju logikę w samych twórcach akcji, tak aby sklepy reagowały tylko na zmiany danych. W przypadku, gdy istnieje logika asynchroniczna, twórca akcji będzie czasami wysyłał wiele akcji do sklepów. Wpadłem na scenariusz nr 2 i przełączyłem się na 'DidMount' (w przypadku asynchronicznego wczytywania danych) lub od czasu do czasu odroczyłem od' setTimeout'. –

+0

@BrandonTilley Objaśniłem oba przykłady, w obu przypadkach ActionCreator, aby pobrać produkty w kategorii, uruchamia asynchroniczną operację interfejsu API. –

+0

@SimenBrekken czy rozwiązałeś swój problem? Można spojrzeć tutaj: http://support.microsoft.com/questions/32537568/flux-waitfor-specific-event? –

Odpowiedz

3

Zawsze, gdy pobierasz stan aplikacji, chcesz pobrać ten stan bezpośrednio ze Sklepów, korzystając z metod pobierających. Akcje są obiektami, które informują Sklepy. Można by pomyśleć o nich jak o prośbie o zmianę stanu. Nie powinni zwracać żadnych danych. Nie są mechanizmem, dzięki któremu powinieneś pobierać stan aplikacji, a jedynie ją zmieniać.

Tak więc w scenariuszu 1, getCurrent(category.id) jest czymś, co należy zdefiniować w sklepie.

W scenariuszu 2 wygląda na to, że napotkasz problem z inicjalizacją danych Sklepu. Zwykle radzę sobie z tym przez (idealnie) pobieranie danych do sklepów przed renderowaniem komponentu root. Robię to w module ładowania. Alternatywnie, jeśli to absolutnie musi być asynchroniczne, możesz utworzyć wszystko, aby pracować z pustym kontem, a następnie ponownie renderować po tym, jak Sklepy zareagują na akcję INITIAL_LOAD.

+2

Objaśniłem oba przykłady, w obu przypadkach ActionCreator, aby pobrać produkty w kategorii, uruchamia asynchroniczną operację interfejsu API. Tak więc w scenariuszu nr 1 najpierw pobieram listę kategorii z mojego API, a następnie produkty z tej kategorii, także za pośrednictwem interfejsu API. Jeśli chodzi o scenariusz nr 2, robię to samo, ale tylko wtedy, gdy komponent jest zamontowany i wymaga danych. Nie wiem, kiedy komponent zostanie zamontowany, więc pobieranie danych do komponentu głównego nie jest możliwe. –

+0

Scenariusz 1: Gdzie jest twórca akcji, który tworzy akcję typu LOAD_CATEGORIES_COMPLETE? Wygląda na to, że wezwanie do API powinno zostać przeniesione do tego twórcy akcji. Nie jest dla mnie jasne, że funkcja LOAD_CATEGORY_PRODUCTS jest przydatna. – fisherwebdev

+0

Scenariusz 2: Twój widok zarządza danymi sklepu. Pozwól sklepowi zarządzać własnymi danymi. – fisherwebdev

0

W scenariuszu 1:

bym wysyłką nową akcję z samego widzenia, więc nowy działania -> dyspozytor -> sklep -> widok cykl wyzwoli.

Mogę sobie wyobrazić, że Twój widok musi pobrać listę kategorii, a także musi domyślnie pokazać listę produktów pierwszej kategorii.

W związku z tym widok będzie reagować na zmiany con najpierw jako kategorię Store. Po załadowaniu listy kategorii uruchom nową akcję, aby uzyskać produkty pierwszej kategorii.

To jest trudna część. Jeśli zrobisz to w odbiorniku zmian w widoku, otrzymasz niezmienny wyjątek, więc tutaj musisz poczekać, aż ładunek pierwszej akcji zostanie całkowicie przetworzony.

Jednym ze sposobów rozwiązania tego problemu jest użycie limitu czasu na zmianę słuchacza widoku. Coś podobnego do wyjaśnionego tutaj: https://groups.google.com/forum/#!topic/reactjs/1xR9esXX1X4, ale zamiast wywoływania akcji ze sklepu, robiłbyś to z widoku.

function getCategoryProducts(id) { 
setTimeout(() => { 
    if (!AppDispatcher.isDispatching()) { 
     CategoryProductActions.get(id); 
    } else { 
     getCategoryProducts(id); 
    } 
}, 3); 
} 

Wiem, to jest okropne, ale przynajmniej nie będziesz mieć sklepów prowadzących do działań lub logiki domeny, które przeciekają twórcom akcji. Dzięki takiemu podejściu działania są "wymagane" od poglądów, które faktycznie ich potrzebują.

Inną opcją, której nie próbowałem szczerze, jest słuchanie zdarzenia DOM po wypełnieniu komponentu z listą kategorii. W tym momencie wyślesz nową akcję, która uruchomi nowy łańcuch "Flux". Uważam, że ten jest lepszy, ale jak powiedziałem, jeszcze nie próbowałem.

Powiązane problemy