AJAX в Flux: обновление хранилищ при изменении зависимого состояния

Я создаю приложение Flux, используя MartyJS (который довольно близок к «ванильному» Flux и использует тот же базовый диспетчер). Он содержит хранилища с неотъемлемыми отношениями зависимости. Например, UserStore отслеживает текущего пользователя, а InstanceStore отслеживает экземпляры данных, принадлежащих текущему пользователю. Данные экземпляра извлекаются из API асинхронно.

Вопрос в том, что делать с состоянием InstanceStore при изменении пользователя.

Я пришел к выводу (например, прочитав ответы @fisherwebdev на SO), что наиболее целесообразно делать запросы AJAX в функции создателя действия и иметь «успешный» результат AJAX в действии, которое, в свою очередь, приводит к изменению магазинов.

Итак, чтобы получить пользователя (то есть войти в систему), я делаю вызов AJAX в функции создателя действия, и когда он разрешается, я отправляю действие RECEIVE_USER с пользователем в качестве полезной нагрузки. UserStore слушает это и соответствующим образом обновляет свое состояние.

Однако мне также нужно повторно получить все данные в InstanceStore, если пользователь изменился.

Вариант 1: я могу прослушивать RECEIVE_USER в InstanceStore и, если это новый пользователь, инициировать запрос AJAX, который, в свою очередь, создает другое действие, которое, в свою очередь, приводит к обновлению InstanceStore. Проблема в том, что это похоже на каскадные действия, хотя технически это асинхронно, поэтому диспетчер, вероятно, разрешит это.

Вариант 2. Другим способом для InstanceStore было бы прослушивание событий изменения, генерируемых UserStore, и затем выполнение танца запрос-действие, но это тоже кажется неправильным.

Вариант 3. Третьим способом для создателя действия может стать организация двух вызовов AJAX и отправка двух действий по отдельности. Однако теперь создатель экшена должен много знать о том, как магазины соотносятся друг с другом.

Один из ответов в Где должен быть сделан запрос ajax в приложении Flux. ? заставляет меня думать, что вариант 1 является правильным, но документы Flux также подразумевают, что хранить триггерные действия нехорошо.


person optilude    schedule 31.01.2015    source источник


Ответы (3)


Что-то вроде варианта 3 кажется мне самым чистым решением, за которым следует вариант 1. Мои рассуждения:

Вариант 2 отличается от ожидаемого способа обработки зависимостей между хранилищами (waitfor), и вам придется проверять после каждого события изменения, чтобы выяснить, какие из них актуальны, а какие можно игнорировать, или начать использовать несколько типов событий; это может стать довольно грязным.

Я думаю, что вариант 1 жизнеспособен; как Билл Фишер заметил в сообщении, на которое вы ссылаетесь, вызовы API можно делать из хранилищ при условии, что результирующие данные обрабатывается путем вызова новых действий. Но «ОК» не обязательно означает «идеально», и вы, вероятно, добьетесь лучшего разделения задач и уменьшите каскадирование, если сможете собрать все вызовы API и инициацию действий в одном месте (например, в ActionCreators). И это соответствовало бы варианту 3.

Однако теперь создатель экшена должен много знать о том, как магазины соотносятся друг с другом.

На мой взгляд, создателю экшена не нужно ничего знать о том, чем занимаются магазины. Ему просто нужно войти в систему пользователя, а затем получить данные, связанные с пользователем. Независимо от того, выполняется ли это с помощью одного вызова API или двух, они логически очень тесно связаны и имеют смысл в рамках одного создателя действия. Как только пользователь вошел в систему и данные получены, вы можете запустить два действия (например, LOGGED_IN, GOT_USER_DATA) или даже одно действие, которое содержит все данные, необходимые для обоих. В любом случае, действия просто повторяют то, что сделали вызовы API, и магазины сами решают, что с этим делать.

Я бы предложил использовать одно действие для обновления обоих хранилищ, потому что это кажется идеальным вариантом использования для waitfor: когда одно действие запускает обработчик в обоих хранилищах, вы можете указать InstanceStore дождаться завершения обработчика UserStore, прежде чем обработчик InstanceStore выполнится. Это будет выглядеть примерно так:

 UserStore.dispatchToken = AppDispatcher.register(function(payload) {
  switch (payload.actionType) {

    case Constants.GOT_USER_DATA:

      ...(handle UserStore response)...

      break;

    ...
  }
});

...

InstanceStore.dispatchToken = AppDispatcher.register(function(payload) {
  switch (payload.actionType) {

    case Constants.GOT_USER_DATA:

      AppDispatcher.waitFor([UserStore.dispatchToken]);

      ...(handle InstanceStore response)...

      break;

    ...
  }
});
person Adam Stone    schedule 05.02.2015

Вариант 1 кажется мне концептуально лучшим выбором. Есть 2 отдельных вызова API, поэтому у вас есть 2 набора событий.

Это множество событий в небольшом объеме кода, но Flux всегда использует простой стандартный подход Action->Store->View. Как только вы сделаете что-то умное (например, вариант 2), вы измените это. Если другие разработчики больше не могут с уверенностью предположить, что любой поток действий работает точно так же, как и любой другой, вы потеряли большое преимущество Flux.

Однако это не будет самым коротким подходом в коде. MartyJS выглядит так, как будто он будет немного лучше, чем собственная библиотека Flux от Facebook!

Другой вариант; если входы в систему всегда должны обновлять InstanceStore, почему бы не включить в вызов API входа в систему все данные InstanceStore?

(И далее; зачем иметь 2 отдельных хранилища? В любом случае они кажутся очень сильно связанными, и нет никаких причин, по которым вы все равно не могли бы совершать вызовы API InstanceStore без повторного вызова входа в систему)

person Michael Martin    schedule 01.02.2015
comment
Причина использования двух хранилищ заключается в том, что все данные каким-то образом привязаны к пользователю (данные в основном представляют собой граф), так что вы либо в конечном итоге получите одну огромную выборку в начале и одно монолитное хранилище, либо вам нужно иметь хранилище. зависимости. - person optilude; 02.02.2015

Я обычно использую обещания для разрешения такой ситуации. Например:

// UserAction.js
var Marty     = require( 'marty' );
var Constants = require( '../constants/UserConstants' );
var vow       = require( 'vow' );

module.exports = Marty.createActionCreators({
    ...

    handleFormEvent: function ( path, e ) {
        var dfd  = vow.defer();
        var prom = dfd.promise();
        this.dispatch( Constants.CHANGE_USER, dfd, prom );
    }
});


// UserStore.js
var Marty     = require( 'marty' );
var Constants = require( '../constants/UserConstants' );

module.exports = Marty.createStore({
    id: 'UserStore',

    handlers: {
        changeUser      : UserConstants.CHANGE_USER
    },


    changeUser: function ( dfd, __ ) {
        $.ajax( /* fetch new user */ )
           .then(function ( resp ) { 
               /* do what you need */
               dfd.resolve( resp ); 
           });
    }
});



// InstanceStore.js
var Marty     = require( 'marty' );
var UserConstants = require( '../constants/UserConstants' );

module.exports = Marty.createStore({
    id: 'InstanceStore',

    handlers: {
        changeInstanceByUser  : UserConstants.CHANGE_USER
    },


    changeInstanceByUser: function ( __, prom ) {
        prom.then(function ( userData ) {
            /* OK, user now is switched */

            $.ajax( /* fetch new instance */ )
               .then(function ( resp ) { ... });
    }
});
person azaviruha    schedule 19.04.2015