Managing store data dependency in React/Flux
Asked Answered
N

3

6

I have a web app developed using Facebook's Flux Architecture. The page has two views: one displays a list of TODO items. The second view displays a random set of TODO items.

There are clearly two concerns that need to be managed by stores. The first is the list of available TODO's. The second is the list of randomly selected TODO items.

I thus have a TODOStore, who's concern is solely of managing the available TODO items. It has actions to loadTODOs, addTODO, deleteTODO, editTODO. On startup, this store does not load all TODO items. I want it retrieve the list of TODO items from the database only when necessary.

The second store is the RandomTODOListStore. It's responsibility is to manage the randomly selected TODO items. Seems to me that the RandomTODOListStore should access the TODO items through the TODOStore, using TODOStore.getTODOItems().

function RandomTODOListStore() {
   var $randomTODOs = [];

   dispatcher.register(function(payload) {
        var action = payload.action;

        switch (action.actionType) {
            case Constants.LOAD_RANDOM_TODO:
                loadRandomTODO();
                break;
        }
    });

    function loadRandomTODO() {
        $randomTODOs = selectRandom(TODOStore.getTODOList());
        emit("change");
    }
}

The issue with this is that, as previously stated, the TODOStore does not load the TODO items on startup.

The question is: "How does the RandomTODOListStore guarantee that the TODOStore has already retrieved the TODO items?".

Nonstriated answered 13/7, 2014 at 14:36 Comment(0)
S
4

The proposed Flux implementation uses a waitFor method to synchronize stores. I created Reflux to handle this much more easily by letting the stores be able to listen to other stores. The effect of that feature is that it will guarantee that the previous store in the chain has handled it's data.

The interface is a bit different, because Reflux doesn't rely on string constants to discern actions so here is an example.

var TodoActions = Reflux.createActions(['load']);

var todoStore = Reflux.createStore({
    init: function() {
        // Listen to the load action
        this.listenTo(TodoActions.load, this.loadActions);
    },
    loadActions: functions() {
        var loadedActions = [];
        // load your actions into loadedActions
        // and do then the following inside the ajax 
        // callback when it is done:
        this.trigger(loadedActions);
    }
});

var randomTodoStore = Reflux.createStore({
    init: function() {
        // You may listen to stores as well
        this.listenTo(todoStore, onLoadedActions);
    },
    onLoadedActions: function(loadedActions) {
        // loaded actions will be passed in from the
        // dotoStores change event trigger

        // you may do your select random from loaded 
        // actions list   
    }
});

// Invoke the action
TodoActions.load();

Hope this makes sense.

Sherman answered 31/7, 2014 at 8:54 Comment(0)
R
2

I think this is why the Flux architecture mention the need to synchronize stores with the waitFor method mentionned here

If you want to sync your stores, your RandomTODOListStore should probably look like:

case Constants.LOAD_RANDOM_TODO:
  Dispatcher.waitFor([TodoStore.dispatcherIndex],this.loadRandomTODO);
  break; 

Then on your TodoStore, you can react to Constants.LOAD_RANDOM_TODO and load the list of todos if they are not loaded yet.

However I think this is overcomplicated, and you should probably not create another store for random todos as this store will (I guess) always delegate to the real todo store. Just use TodoStore.getRandom() instead

Edit:

Yes TodoStore does not need to know how it is used. Thus when you want to get a random todo, you can probably use the same event you use, like Constants.LOAD_TODOS and then get a random todo from that store with TodoStore.getTodos()[randomIndex]

If both components fire the event concurrently, you can ignore the 2nd event

Another solution is to have the RandomTODOListStore react to the loaded event and select some todos of the main store to put them in the RandomTODOListStore:

case Constants.TODOS_LOADED:
  this.randomTodos = randomSubset(TodoStore.getTodos,subsetSize);
  break; 

You can inspire yourself from the Async exemple from Fluxxor: for async data, there's not only one event that is fired but 2 (command + success/failure)

Rockhampton answered 21/7, 2014 at 11:43 Comment(1)
Yes, I did think about that. The problem there is that TODOStore shouldn't care about how others are using it. It shouldn't have to react to RandomTODOListStore actions. Imagine that every time I have something else that needs TODO items, TODOStore is going to get bloated very fast.Nonstriated
S
0

your RandomTODOListStore needs to wait for TODOStore. You can achieve this easily using this library(mine) https://github.com/kjda/ReactFlux

var ReactFlux = require('react-flux');

var TODOConstants = ReactFlux.createConstants(['LOAD'], 'TODO');

var TODOActions = ReactFlux.createActions({
    load: [TODOConstants.TODO_LOAD, function(){
      //load your todos here
      return { todos: [{title: 'do this'}, {title: 'do that'}]}
    }]
});

var TODOStore = ReactFlux.createStore({
    getTodos: function(){
       return this.getState().todos;
    }
}, [
   [TODOConstants.TODO_LOAD_SUCCESS, function(payload){
      this.setState({
          todos: payload.todos
      });
   }]
]);

var RandomTODOListStore = ReactFlux.createStore({
    selectRandomFromList: function(todos){
      //..... select some here
    }
}, [

   /**
    * This waits for TODOStore to process the message first
    */
   [TODOConstants.TODO_LOAD_SUCCESS, [TODOStore], function(payload){
        this.selectRandomFromList(payload.todos);
   }]

]);

now when your app starts you just need to call the action

TODOActions.load();

Sculpturesque answered 27/7, 2014 at 22:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.