Search code examples
reactjs-fluxflux

How to avoid action chains


I'm trying to understand Flux pattern.

I believe that in any good design the app should consist of relatively independent and universal (and thus reusable) components glued together by specific application logic.

In Flux there are domain-specific Stores encapsulating data and domain logic. These could be possibly reused in another application for the same domain.

I assume there should also be application-specific Store(s) holding app state and logic. This is the glue.

Now, I try to apply this to imaginary "GPS Tracker" app:

...

When a user clicks [Stop Tracking] button, corresponding ViewController raises STOP_CLICK.

  • AppState.on(STOP_CLICK):

    • dispatch(STOP_GEOLOCATION)
    • dispatch(STOP_TRACKING)
  • GeolocationService.on(STOP_GEOLOCATION):

    • stopGPS(); this.on = false; emit('change')
  • TrackStore.on(STOP_TRACKING):

    • saveTrack(); calcStatistics(); this.tracking = false; emit('change')
    • dispatch(START_UPLOAD)

So, I've got an event snowball.

It is said that in Flux one Action should not raise another. But I do not understand how this could be done.

I think user actions can't go directly to domain Stores as these should be UI-agnostic. Rather, AppState (or wherever the app logic lives) should translate user actions into domain actions.

  1. How to redesign this the Flux way?
  2. Where should application logic go?
  3. Is that correct to try to keep domain Stores independent of the app logic?
  4. Where is the place for "services"?

Thank you.


Solution

  • All of the application logic should live in the stores. They decide how they should respond to a particular action, if at all.

    Stores have no setters. The only way into the stores is via a dispatched action, through the callback the store registered with the dispatcher.

    Actions are not setters. Try not to think of them as such. Actions should simply report on something that happened in the real world: the user interacted with the UI in a certain way, the server responded in a certain way, etc.

    This looks a lot like setter-thinking to me:

    dispatch(STOP_GEOLOCATION)

    dispatch(STOP_TRACKING)

    Instead, dispatch the thing that actually happened: STOP_TRACKING_BUTTON_CLICKED (or TRACKING_STOPPED, if you want to be UI-agnostic). And then let the stores figure out what to do about it. All the stores will receive that action, and they can all respond to it, if needed. The code you have responding to two different actions should be responding to the same action.

    Often, when we find that we want dispatch within a dispatch, we simply need to back up to the original thing that happened and make the entire application respond to that.