What if two Flux stores have to depend on each other
Asked Answered
B

2

7

One of the goals of Flux is to make the app more predictable by reducing crazy tangled dependencies. Using the Dispatcher you can define a strict order in which the Stores are updated. That creates a nice tree dependency hierarchy. That's the theory. Consider following situation:

I have a game. The store sitting at the top of the hierarchy is StateStore that holds only the current game state, i. e. playing, paused, over. It is updated via actions like PAUSE or RESUME. All other stores depend on this one. So when a store handles some kind of update action (i. e. MOVE_LEFT), it first checks the StateStore and if the game is paused or over, it ignores the action.

Now let's say that there is an action that would cause game over. It updates some store and the store decides that the game shouldn't continue ("the game character moves left and falls into a trap"). So the state in the StateStore should change to over. How do I do that?

Theoretically, it should go like this:

  1. The given store is updated first and reaches the point of game over
  2. The StateStore is updated afterwards (it waitsFor the other store), checks the other store and switches the state to over.

Unfortunately, the other store needs to access the StateStore as well to check the current game state to see if it should be updated at all (i. e. the game is not paused). They clearly depend on each other.

Possible solutions:

  1. Merge such stores into one store. That would probably cause my whole app to collapse into a single store which brings up the question whether Flux in this case is a good idea.
  2. Distinguish update order and read-only dependencies. All stores would be updated in a strict order, however they could read from each other arbitrarily. The StateStore would therefore for every action check all existing stores and if any of them indicated game over, it would change the state to over, effectively preventing all other stores from updating.

What do you think?

Bipetalous answered 14/6, 2015 at 21:35 Comment(2)
Did you figure out a way through this? I'm kinda having the same issue at the moment..Belvia
I added an answer which might help you.Bipetalous
Z
2

In Flux stores should be as independent from each other as possible and should not read from each other. The only way to change their state is through actions.

In your case, if some store decides that the game is over — you should update a StateStore from the ActionCreator. You can do it by calling a HaltGameActionCreator from the store or by dispatching a HALT_GAME action from ActionCreator that triggered the store change in the first place.

Zipah answered 19/6, 2015 at 13:33 Comment(2)
Reading and changing are two different things. In my example, I don't need to change one store from another, I need to read from another. Also it is not allowed to dispatch a new action while handling the previous action and going around this by dispatching the action asynchronously seems a bit hacky. I could check the stores for game over after dispatching MOVE_LEFT in the creator and consequently dispatch HALT_GAME if needed but that doesn't seem right either. If MOVE_LEFT causes game over, the application state (StateStore) should contain that information, I shouldn't need another action.Bipetalous
@Zipah "stores should be as independent from each other as possible": but Dispatcher has a waitFor method which is hardly used in all facebook examplesCourtneycourtrai
B
1

For those having the same issue, you can read here about the actual application I had this problem with and how I approached it. Long story short, I allowed all the stores to arbitrarily read from each other (the suggested solution no. 2).

Note that ES6 modules allow circular dependencies which simplifies the implementation.

Nevertheless, looking back I'm not sure if it was a right decision. If a piece of business logic inherently contains a circular dependency, we should not try to apply a solution that doesn't really support it just because somebody says so. Flux is only one pattern, there are many other ways how to structure the code. So perhaps I would recommend collapsing the whole logic into a single store and use one of the other ways to implement the store itself (e.g. standard OOP techniques).

I would also consider using redux with reselect instead of Flux. The problem with the original example is with the StateStore that depends on two different inputs. It can be changed either by the user explicitly pausing/resuming the game, or by the game situation reaching game over. The advantage of this approach is that you need to check only one store to get the current game state.

With redux/reselect, you'd have one reducer handling pause/resume actions and another reducer handling the game situation. Then you'd have a selector combining these two pieces of information into the final game state. Most of the business logic would be moved from the stores to action creators, i.e., in the moveLeft() action creator, you'd use this selector to check the game state and only then you'd dispatch MOVE_LEFT action.

Note that this is just a rough idea and I don't know if it's viable.

Bipetalous answered 10/7, 2016 at 20:0 Comment(2)
Interesting, so would you allow a store to trigger a dispatch? That is, stores to run actions. The implication here being that stores can send actions to each other..Belvia
No, I wrote read. They can read from each other. A store cannot dispatch an action and the default Facebook implementation of Flux won't even allow it. You could get around this limitation by dispatching a sequence of actions inside an action creator but in this case it is also not a nice solution. If one particular user interaction causes game over, you should not need two actions to get the state updated.Bipetalous

© 2022 - 2024 — McMap. All rights reserved.