Асинхронная загрузка данных в хранилище потоков

Скажем, у меня есть TodoStore. TodoStore отвечает за хранение моих предметов TODO. Элементы Todo хранятся в базе данных.

Я хочу знать, каков рекомендуемый способ загрузки всех элементов todo в магазин и как представления должны взаимодействовать с магазином для загрузки элементов TODO при запуске.

Первая альтернатива - создать действие loadTodos, которое будет извлекать Todos из базы данных и генерировать событие TODOS_LOADED. Затем представления будут вызывать действие loadTodos, а затем прослушивать событие TODOS_LOADED и затем обновляться, вызывая TodoStore.getTodos().

Другой альтернативой является отсутствие действия loadTodos и наличие TodoStore.getTodos(), которое будет возвращать обещание с существующими элементами TODO. Если TodoStore уже загрузил элементы TODO, он просто возвращает их; если нет, то он запросит из базы данных и вернет извлеченные элементы. В этом случае, даже если хранилище уже загрузило элементы TODO, оно не будет генерировать событие TODOS_LOADED, поскольку getTodos не является действием.

function getTodos() {
   if (loaded)
      return Promise.resolve($todoItems);
   else
      return fetchTodoItemsFromDatabase().then(todoItems) {
         loaded = true;
         $todoItems = todoItems;
         return $todoItems;
      });
}

Я уверен, что многие скажут, что это нарушает архитектуру Flux, потому что функция getTodos изменяет состояние хранилища, и состояние хранилища следует изменять только с помощью действий, отправленных диспетчером.

Однако, если вы считаете, что состояние для TodoStore - это существующие элементы TODO в базе данных, то getTodos на самом деле не меняет никакого состояния. Элементы TODO точно такие же, поэтому нет необходимости обновлять или уведомлять представление. Единственное, что теперь хранилище уже извлекло данные, поэтому они теперь кэшируются в хранилище. С точки зрения View, его не должно волновать, как реализован Магазин. Это не должно заботить, нужно ли магазину получать данные из базы данных или нет. Все представления заботятся о том, чтобы они могли использовать Магазин для получения элементов TODO и что Магазин будет уведомлять их, когда новые элементы TODO создаются, удаляются или изменяются.

Следовательно, в этом сценарии представления должны просто вызывать TodoStore.getTodos() для визуализации себя при загрузке и регистрировать обработчик событий в TODO_CHANGE, чтобы получать уведомления, когда им необходимо обновить себя из-за добавления, удаления или изменения.

Что вы думаете об этих двух решениях. Это какие-то другие решения?

Ответ 1

Представления не должны быть объектами, которые вызывают loadTodos(). Это может произойти в файле начальной загрузки.

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

Что напоминает Flux в вашем решении fetchTodoItemsFromDatabase(), так это то, что никакой другой объект не устанавливает данные в хранилище. Магазин обновляется сам. Это хорошо.

Моя единственная серьезная критика этого решения заключается в том, что оно может привести к задержке рендеринга, если вы фактически получаете исходные данные с сервера. В идеале вы должны отправить некоторые данные с помощью HTML. Вы также хотели бы убедиться, что вы вызываете данные хранилища в методе getInitialState() вашего контроллера-представления.

Ответ 2

Вот мое мнение об этом, очень близкое к вашему.

  • Я поддерживаю состояние моей заявки в Store через Immutable.Record и Immutable.OrderedMap из Immutable.js
  • У меня есть верхний компонент контроллера, который получает свое состояние из магазина.

Что-то вроде следующего:

function getInitialState() {
    return {
        todos: TodoStore.getAll()
    }
}
  • Методы TodoStore.getAll будут извлекать данные с сервера с помощью запроса APIUtils.getTodos(), если его внутренняя карта _todos пуста. Я выступаю за чтение данных, активированных в Store, и запись данных, инициированных в ActionCreators.
  • К тому времени, когда запрос обрабатывается, мой компонент будет отображать простой спиннер загрузки или что-то в этом роде
  • Когда запрос разрешается, APIUtils запускает действие, такое как TODO_LIST_RECEIVE_SUCCESS или TODO_LIVE_RECEIVE_FAIL, в зависимости от состояния ответа
  • Мой TodoStore будет реагировать на это действие, обновляя его внутреннее состояние (заполняя его внутренним Immutable.OrderedMap с помощью Immutable.Record, созданного из полезных нагрузок действия.

Если вы хотите увидеть пример с базовой реализацией, посмотрите этот ответ о React/Flux и xhr/routing/caching.

Ответ 3

Я знаю, что прошло несколько лет с тех пор, как об этом спросили, но это прекрасно подвело итог тем вопросам, с которыми я борюсь на этой неделе. Поэтому, чтобы помочь другим, кто может столкнуться с этим вопросом, я нашел этот пост в блоге, который действительно помог мне Ника Клепингера: "ngrx и Tour of Heroes".

Он специально использует Angular 2 и @ngrx/store, но очень хорошо отвечает на ваш вопрос.