Flux store depends on an asynchronous download in another store - reactjs

I'm learning React and flux.
Say there's a route like
// Route
/continent/:continentId/countries
// Example
/continent/europe/countries
There are two stores, a ContinentsStore and a CountriesStore
What's the best design pattern, in Flux, so that when the route is loaded, the ContinentsStore asynchronously fetches a list of all the continents, sets the current one to Europe, and then CountriesStore looks for the current continent in ContinentsStore, then proceeds to download a list of all the countries in that Continent.
Specifically, where are the action creators, and what might the actions types be?
Thanks.

There is an aggregation store concept in reflux.js. So one store can listen to another one and provides additional logic that depends on data changes in an observable store. This approach is useful for data aggregation and chaining operations.

Related

Why do we need dispatcher in Flux?

This is not a React specific question. I'm thinking of implementing Flux in Aurelia/Angularjs.
While reading up on flux, I'm not convinced of the need of the dispatcher step. Why can't a component call the store directly to update and retrieve data? Is there anything wrong with that approach?
For example: If I have a CarStore that can create new cars, update cars and get a list of cars(just a thin layer on the CRUD api), I should be able to retrieve/update the list by directly calling the store from the car-grid component. Since the store is a singleton, whenever the list updates, car-grid should automatically get the new items. What is the benefit of using a dispatcher in this scenario?
I've created several large apps using React-native with Redux as the store / view state updater.
The dispatch action is synchronous regardless. There's a big disadvantage to using dispatchers, you lose the function signature. (Debugging, auto-catching type-errors, refactoring lost, multiple declarations of the same function, list goes on)
Never had to use a dispatcher and its caused no issues. Within the actions we simply call getState().dispatch. The store is a singleton anyhow, it's heavily recommended you don't have multiple stores. (Why would you do that...)
You can see here why are dispatchers important (check out the section Why We Need a Dispatcher). The way I see it, the idea is basically being able to access to various stores in a synchronous way (one callback finishes before another one is called). You can make this thanks to the waitFor method, which allows you to wait for a store to finish processing an action (or more tan one). There is a good example in the docs. For example, your application may grow and instead of having just that CarStore you have another Store whose updates depend on the CarStore updates.
If you will only ever have one store, then a dispatcher is redundant in my opinion. If you have multiple stores however, then a dispatcher is important so that actions don't need to know about each of these stores.
Please note that I am not saying that you should ditch the dispatcher if you only have one store. It's still a good pattern as it gives you the option of supporting multiple stores if you ever need to in the future.

React/Flux efficiency: how to prevent an action from triggering multiple renders of a component?

I'm still studying the Flux architecture, and noticed that:
one Action can cause multiple Stores to emit a "change" event
one ControllerView can be subscribed to the "change" event of multiple Stores
So, if ControllerView depends on data of two Stores, and those two Stores are both changed by one Action, the ContollerView - with all its components - will be rendered (to the virtual DOM) twice, the first time with incomplete data.
Is there any recognized pattern to avoid this? I can think of some simple solutions, but I wouldn't like to reinvent the wheel.
In general, you should just allow it to render more than once. However, the if the action always triggers actions in both stores you can use the "waitFor()" method of the dispatcher to let one store update first, then only emit a change when the second store gets updated.
This is only useful if the action will always affect both stores, however.
Best practice in flux pattern is to limit number of stateful components, and try only to have top component listen to stores, and send down all relevant info in props.
With multiple stores, one solution to minimize multiple renders after a single change:
create a StatStore that stores nothing, but listens to all relevant Actions, and waits for all other relevant stores.
this StatStore has getter functions, that collect (and possibly calculates stats) from other stores
your top component only listens to StatStore change emissions
top component then gets data from StatStore.
That way, a single change only results in one re-render.
I'll add this for future reference: I didn't find any good solution to this problem with vanilla Flux, and in the end switched to Redux, which has all the advantages of Flux and doesn't suffer from this disadvantage.

In flux, why Store is needed?

According to Flux Architecture View uses Action to call Dispatcher that updates the Store, while View listening to Store change events.
My question is: Why do we need Store?
In order to list all users, my Component will call ListAllUsersAction that will in turn call my API and will update the Store with the result of API call. Store then emits change event that the View is listening to. But the store also stores the result. Why? Why this middle layer is needed? I wont call the store directly anyway, so this cache layer makes no sense to me, and as I generate more events that loads more data, eventually all my stores will have all the state of my application because flux architecture says nothing about cleaning the Stores.
Am I missing something?
The goal of Flux is to keep data flow easy to understand even as apps becomes large and complex, such that a new person can be brought up to speed quickly, figure out what's going on by inspecting the source code, and be confident that they can make changes without breaking things. Modularity and separation of concerns are a big part of that. The Stores are a way to keep the data models independent of the details of the view layer and establish a single source of truth for the application state. You can look at any Store's code and see what data it holds, what actions it responds to, which dependencies it has for data in other Stores. It's a matter of organization for the sake of the developers, at the cost of code being slightly less compact.
In order to list all users, my Component will call ListAllUsersAction
that will in turn call my API and will update the Store with the
result of API call.
Since the function of Actions is mainly to provide updated data to stores, you could also just call the API first and then just create one Action to handle the result.
as I generate more events that loads more data, eventually all my
stores will have all the state of my application because flux
architecture says nothing about cleaning the Stores.
Holding the current state of the application is the intended function of the Stores. If user actions or API calls cause the data to change, the Actions notify the Stores and the Stores responsible for keeping that data update accordingly (maybe even being reset to null). There's no need for any other sort of cleaning, because the Stores "having all the state" is exactly what they're supposed to be doing.
Stores are in charge of application state and logic, so for example, let's say you fetch all users through your ListAllUsersAction, you get an array from your API
var users = [{firstName: 'LIMELIGHTS'}, {firstName: 'SKWEE357'}];
Now, the users name are apparently capitalised as your API decides that this is the way to deliver the data.
This just won't do so you want to fix it.
Using just React or just the Action where would you put this code, where would it make sense?
In your view, your dispatcher or your action? No, you definitely don't want to clutter your React component with this type of logic.
Nor does it make sense to do this data manipulation in the Dispatcher or Action, they are after all just notifiers that something should happen.

Why should I use Actions in Flux?

An application that I develop was initially built with Flux.
However, over time the application became harder to maintain. There was a very large number of actions. And usually one action is only listened to in one place (store).
Actions make it possible to not write all event handler code in one place. So instead of this:
store.handleMyAction('ha')
another.handleMyAction('ha')
yetAnotherStore.handleMyAction('ha')
I can write:
actions.myAction('ha')
But I never use actions that way. I am almost sure, that this isn't an issue of my application.
Every time I call an action, I could have just called store.onSmthHappen instead of action.smthHappen.
Of course there are exceptions, when one action is processed in several places. But when that happens it feels like something went wrong.
How about if instead of calling actions I call methods directly from the store? Will my application not be so flexible? No! Occurs just rename (with rare exceptions). But at what cost! It becomes much harder to understand what is happening in the application with all these actions. Each time, when tracking the processing of complex action, I have to find in stores where they are processed. Then in these Stores I should find the logic that calls another action. Etcetera.
Now I come to my solution:
There are controllers that calls methods from stores directly. All logic to how handle action is in the Store. Also Stores calls to WebAPI (as usually one store relating to one WebAPI). If the event should be processed in several Stores (usually sequentially), then the controller handles this by orchestrating promises returned from stores. Some of sequentials (common used) in private methods of itself. And method of controllers can use them as simple part of handling. So I will never be duplicating code.
Controller methods do not return anything (one-way flow).
In fact the controller does not contain the logic of how to process the data. It's only points where, and in what sequence.
You can see almost the complete picture of the data processing in the Store. There is no logic in stores about how to interact with another stores (with flux it's like a many-to-many relation but just through actions). Now the store is a highly cohesive module that is responsible only for the logic of domain model (collection).
The main (in my opinion) advantages of flux are still here.
As a result, there are Stores, which are the only true source of the data. Components can subscribe to the Stores. And the components calls the same methods as before, but instead of actions uses controller. Interaction with React did not change at all.
Also, event processing becomes much obvious. Now I can just look at the handler in the controller and all becomes clear, and it's much easier to debug.
The question is:
Why were actions created in flux? And what are their advantages that I have missed?
Actions where implemented to capture a certain interaction on the view or from the server which can then be dispatched to as many different stores as you like. The developers explained this with the example of the facebookchat.
There is a messageStore and a threadstore. When the action eg. messagePost was emitted it got dispatched into both stores doing different work to update their attributes. Threadstore increased the number of unread messages and messageStore added the new message to its messagearray.
So its basicly channeling one action to perform datachanges in more than one store.
I had the same questions and thought process as you, and now I started using Flummox which makes it cleaner to have the Flux architecture.
I define my Actions in the same file where I define my Store, and that's close enough. I can still subscribe to the dispatcher to log events so I can see all actions being called, and I have the option to create multi-store Actions if needed.
It comes with a nice FluxComponent that lets you wrap all store-related code in a single place so its children are stateless components that get updated on store changes, like
<FluxComponent connectToStores={['storeA', 'storeB']}>
<InnerComponent />
</FluxComponent>
Keeping the Flux architecture (it uses Facebook's Flux behind the scenes) will hopefully make it easy to use other technologies like GraphQL.

Should flux stores, or actions (or both) touch external services?

Should the stores maintain their own state and have the ability to call network and data storage services in doing so ...in which case the actions are just dumb message passers,
-OR-
...should the stores be dumb recipients of immutable data from the actions (and the actions be the ones that fetch/send data between external sources? Store in this instance would act as view-models and would be able to aggregate / filter their data prior to setting their own state base on the immutable data they were fed by the action.
It seems to me that it should be one or the other (rather than a mix of both). If so, why is one preferred / recommended over the other?
I've seen the flux pattern implemented both ways, and after having done both myself (initially going with the former approach), I believe that stores should be dumb recipients of data from the actions, and that asynchronous processing of writes should live in the action creators. (Async reads can be handled differently.) In my experience, this has a few benefits, in order of importance:
Your stores become completely synchronous. This makes your store logic much easier to follow and very easy to test—just instantiate a store with some given state, send it an action, and check to see if the state changed as expected. Furthermore, one of the core concepts in flux is to prevent cascading dispatches and to prevent multiple dispatches at once; this is very difficult to do when your stores do asynchronous processing.
All action dispatches happen from the action creators. If you handle asynchronous operations in your stores and you want to keep your stores' action handlers synchronous (and you should in order to get the flux single-dispatch guarantees), your stores will need to fire additional SUCCESS and FAIL actions in response to asynchronous processing. Putting these dispatches in the action creators instead helps separate the jobs of the action creators and the stores; furthermore, you don't have to go digging through your store logic to figure out where actions are being dispatched from. A typical asynchronous action in this case might look something like this (change the syntax of the dispatch calls based on the flavor of flux you're using):
someActionCreator: function(userId) {
// Dispatch an action now so that stores that want
// to optimistically update their state can do so.
dispatch("SOME_ACTION", {userId: userId});
// This example uses promises, but you can use Node-style
// callbacks or whatever you want for error handling.
SomeDataAccessLayer.doSomething(userId)
.then(function(newData) {
// Stores that optimistically updated may not do anything
// with a "SUCCESS" action, but you might e.g. stop showing
// a loading indicator, etc.
dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
}, function(error) {
// Stores can roll back by watching for the error case.
dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
});
}
Logic that may otherwise be duplicated across various actions should be extracted into a separate module; in this example, that module would be SomeDataAccessLayer, which handles doing the actual Ajax request.
You need less action creators. This is less of a big deal, but nice to have. As mentioned in #2, if your stores have synchronous action dispatch handling (and they should), you'll need to fire extra actions to handle the results of asynchronous operations. Doing the dispatches in the action creators means that a single action creator can dispatch all three action types by handling the result of the asynchronous data access itself.
I tweeted this question to the devs at Facebook and the answer I got from Bill Fisher was:
When responding to a user's interaction with the UI, I would make the async call in the action creator methods.
But when you have a ticker or some other non-human driver, a call from the store works better.
The important thing is to create an action in the error/success callback so data always originates with actions
The stores should do everything, including fetching data, and signalling to components that the store's data has been updated. Why? Because actions can then be lightweight, disposable and replaceable without influencing important behavior. All important behavior and functionality happen in the store. This also prevents duplication of behavior that would otherwise be copied in two very similar but different actions. The stores are your single source of (handling the) truth.
In every Flux implementation I've seen Actions are basically event strings turned into objects, like traditionally you'd have an event named "anchor:clicked" but in Flux it would be defined as AnchorActions.Clicked. They're even so "dumb" that most implementations have separate Dispatcher objects to actually dispatch the events to the stores that are listening.
Personally I like Reflux' implementation of Flux where there are no separate Dispatcher objects and Action objects do the dispatching themselves.
edit: Facebook's Flux actually fetches in "action creators" so they do use smart actions. They do also prepare the payload using the stores:
https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27 (line 27 and 28)
The callback on completion would then trigger a new action this time with the fetched data as payload:
https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51
So I guess that's the better solution.
I'll provide an argument in favor of "dumb" Actions.
By placing the responsibility for collecting view data in your Actions, you couple your Actions to the data requirements of your views.
In contrast, generic Actions, that declaratively describe the intent of the user, or some state transition in your application, allows any Store that responds to that Action to transform the intent, into state tailored specifically for the views subscribed to it.
This lends itself to more numerous, but smaller, more specialized Stores. I argue for this style because
this gives you more flexibility in how views consume Store data
"smart" Stores, specialized for the views that consume them, will be smaller and less coupled for complex apps, than "smart" Actions, on which potentially many views depend
The purpose of a Store is to provide data to views. The name "Action" suggests to me that its purpose is to describe a change in my Application.
Suppose you have to add a widget to an existing Dashboard view, which shows some fancy new aggregate data your backend team just rolled out.
With "smart" Actions, you might need to change your "refresh-dashboard" Action, to consume the new API. However, "Refreshing the dashboard" in an abstract sense has not changed. The data requirements of your views is what has changed.
With "dumb" Actions, you might add a new Store for the new widget to consume, and set it up so that when it receives the "refresh-dashboard" Action type, it sends a request for the new data, and exposes it to the new widget once it's ready. It makes sense to me that when the view layer needs more or different data, the things that I change are the sources of that data: Stores.
gaeron's flux-react-router-demo has a nice utility variation of the 'correct' approach.
An ActionCreator generates a promise from an external API service, and then passes the promise and three action constants to a dispatchAsync function in a proxy/extended Dispatcher. dispatchAsync will always dispatch the first action e.g. 'GET_EXTERNAL_DATA' and once the promise returns it will dispatch either 'GET_EXTERNAL_DATA_SUCCESS' or 'GET_EXTERNAL_DATA_ERROR'.
If you want one day to have a development environment comparable to what you see in Bret Victor's famous video Inventing on Principle, you should rather use dumb stores that are just a projection of actions/events inside a data structure, without any side effect. It would also help if your stores were actually member of the same global immutable data structure, like in Redux.
More explainations here: https://stackoverflow.com/a/31388262/82609

Categories

Resources