Asynchronous data loading in flux stores
Asked Answered
N

3

11

Say I have a TodoStore. The TodoStore is responsible for keeping my TODO items. Todo items are stored in a database.

I want to know what is the recommended way for loading all todo items into the store and how the views should interact with the store to load the TODO items on startup.

The first alternative is to create a loadTodos action that will retrieve the Todos from the database and emit a TODOS_LOADED event. Views will then call the loadTodos action and then listen to the TODOS_LOADED event and then update themselves by calling TodoStore.getTodos().

Another alternative is to not have a loadTodos action, and have a TodoStore.getTodos() that will return a promise with the existing TODO items. If the TodoStore has already loaded the TODO items, it just returns them; if not, then it will query from the database and return the retrieved items. In this case, even though the store now has loaded the TODO items, it will not emit a TODOS_LOADED event, since getTodos isn't an action.

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

I'm sure many will say that that breaks the Flux architecture because the getTodos function is changing the store state, and store state should only be changed though actions sent in from the dispatcher.

However, if you consider that state for the TodoStore is the existing TODO items in the database, then getTodos isn't really changing any state. The TODO items are exactly the same, hence no view need to be updated or notified. The only thing is that now the store has already retrieved the data, so it is now cached in the store. From the View's perspective, it shouldn't really care about how the Store is implemented. It shouldn't really care if the store still needs to retrieve data from the database or not. All views care about is that they can use the Store to get the TODO items and that the Store will notify them when new TODO items are created, deleted, or changed.

Hence, in this scenario, views should just call TodoStore.getTodos() to render themselves on load, and register an event handler on TODO_CHANGE to be notified when they need to update themselves due to a addition, deletion, or change.

What do you think about these two solutions. Are they any other solutions?

Nathanson answered 10/7, 2014 at 19:26 Comment(0)
H
3

The views do not have to be the entities that call loadTodos(). This can happen in a bootstrap file.

You're correct that you should try your best to restrict the data flow to actions inside the dispatch payload. Sometimes you need to derive data based on the state of other stores, and this is what Dispatcher.waitFor() is for.

What is Flux-like about your fetchTodoItemsFromDatabase() solution is that no other entity is setting data on the store. The store is updating itself. This is good.

My only serious criticism of this solution is that it could result in a delay in rendering if you are actually getting the initial data from the server. Ideally, you would send down some data with the HTML. You would also want to make sure to call for the stores' data within your controller-views' getInitialState() method.

Hazzard answered 13/8, 2014 at 17:27 Comment(0)
C
0

Here is my opinion about that, very close to yours.

  • I maintain the state of my application in Store via Immutable.Record and Immutable.OrderedMap from Immutable.js
  • I have a top controller-view component that get its state from the Store.

Something such as the following :

function getInitialState() {
    return {
        todos: TodoStore.getAll()
    }
}
  • TodoStore.getAll methods will retrieve the data from the server via a APIUtils.getTodos() request if it's internal _todos map is empty. I advocate for read data triggered in Store and write data triggered in ActionCreators.
  • By the time the request is processing, my component will render a simple loading spinner or something like that
  • When the request resolves, APIUtils trigger an action such as TODO_LIST_RECEIVE_SUCCESS or TODO_LIVE_RECEIVE_FAIL depending on the status of the response
  • My TodoStore will responds to these action by updating its internal state (populating it's internal Immutable.OrderedMap with Immutable.Record created from action payloads.

If you want to see an example through a basic implementation, take a look to this answer about React/Flux and xhr/routing/caching .

Cipango answered 26/8, 2015 at 15:50 Comment(0)
T
0

I know it's been a couple of years since this was asked, but it perfectly summed up the questions I am struggling with this week. So to help any others that may come across this question, I found this blog post that really helped me out by Nick Klepinger: "ngrx and Tour of Heroes".

It is specifically using Angular 2 and @ngrx/store, but answers your question very well.

Tillo answered 5/4, 2017 at 15:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.