This is an advanced example. It pulls a lot of the concepts from previous examples into a single application. This implements TodoMVC where the data is persisted and requested through a simple server. The server simulates delays and errors. In the example we will handle things like optimistic updates, loading states, and failing API requests.
This is just one way to handle asynchronous actions. It is not the only way, and may not be the best way. This is simply how we deal with asynchronous actions most commonly at Facebook.
cd path/to/flux-async npm install # builds client code when it changes (npm run build to build just once) npm run watch # starts server, code is at ./server/app.js npm run start # this will run flow and make sure there are no type errors in the code flow # navigate to: http://localhost:3000/home/ to use the todo application
In this example we introduce DataManagers. This is how to communicate with a server in Flux. A DataManager translates input arguments into an action. The resulting action should always be dispatched asynchronously. This allows time for an API request in before dispatching the action.
DataManagers should only be called from stores.
LoadObjects are immutable objects that represent a piece of data that has to
be requested from the server. It can have any combination of:
operation. We can represent many things, some examples:
A piece of data we just requested: (
Data that has had an error: (
Data that we are retrying: (
Normal piece of data: (
Data that we are updating: (
And many others states your data might be in. There are also several supporting
LoadObjectMap is an immutable map that has
LoadObject values. When a key is
requested that does not exist instead of returning null it will return an empty
LoadObject, this helps prevent the need for null-checking when we can use
LoadObject's to represent the absence of a value. Additionally on construction
you can provide a function that will be called with keys that are requested that
do not yet have a value. This makes it easy to figure out when to load data from
the server. Generally you dispatch an action with the missing keys so that a
store will start loading them.
LoadObjectState provides similar functionality to
LoadObjectMap but for
when your state is a single
ID's are always created on the server, so in order to optimistically update we need a service to generate some fake IDs. We just use a really simple utility that also has a mechanism for testing if IDs are fake. Without this it would be hard to provide optimistic updates and properly correlate responses with data that we are already rendering.
List stores are a kind of store responsible for keeping track of a list of IDs. They only store IDs. They should never store lists of the objects themselves, that should always be in another store that keeps track of a map of those objects. This separates the concerns of simply loading the object data, and figuring out in which order it should render, dealing with sorting, filtering, etc. This makes it possible for the ordering to change without having to re-render views that use the unchanged data.
Instead of using action-creators we dispatch our actions manually. This only
works because we have strongly typed every possible action in
This means that Flow will have an error if we ever try to dispatch something
that is invalid. This also allows us to refine actions in our reduce functions
and make sure we are accessing valid properties based on what kind of action
we are dealing with.