• Stars
    star
    38
  • Rank 685,261 (Top 14 %)
  • Language
    JavaScript
  • Created almost 10 years ago
  • Updated almost 10 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Flux TodoMVC Example

An application architecture for React utilizing a unidirectional data flow.

Learning Flux

The React website is a great resource for getting started.

Read the blog post announcing Flux: "An Application Architecture for React", then read more about the Flux architecture and a TodoMVC tutorial explaining the structure of the files in this folder.

Overview

Flux applications have three major parts: the dispatcher, the stores, and the views (React components). These should not be confused with Model-View-Controller. Controllers do exist in a Flux application, but they are controller-views -- views often found at the top of the hierarchy that retrieve data from the stores and pass this data down to their children. Additionally, actions โ€” dispatcher helper methods โ€” are often used to support a semantic dispatcher API. It can be useful to think of them as a fourth part of the Flux update cycle.

Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with a React view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. This works especially well with React's declarative programming style, which allows the store to send updates without specifying how to transition views between states.

We originally set out to deal correctly with derived data: for example, we wanted to show an unread count for message threads while another view showed a list of threads, with the unread ones highlighted. This was difficult to handle with MVC โ€” marking a single thread as read would update the thread model, and then also need to update the unread count model. These dependencies and cascading updates often occur in a large MVC application, leading to a tangled weave of data flow and unpredictable results.

Control is inverted with stores: the stores accept updates and reconcile them as appropriate, rather than depending on something external to update its data in a consistent way. Nothing outside the store has any insight into how it manages the data for its domain, helping to keep a clear separation of concerns. This also makes stores more testable than models, especially since stores have no direct setter methods like setAsRead(), but instead have only an input point for the payload, which is delivered through the dispatcher and originates with actions.

Structure and Data Flow

Data in a Flux application flows in a single direction, in a cycle:

Views ---> (actions) ----> Dispatcher ---> (registered callback) ---> Stores -------+
ษ…                                                                                   |
|                                                                                   V
+-- (Controller-Views "change" event handlers) ---- (Stores emit "change" events) --+

A unidirectional data flow is central to the Flux pattern, and in fact Flux takes its name from the Latin word for flow. In the above diagram, the dispatcher, stores and views are independent nodes with distinct inputs and outputs. The actions are simply discrete, semantic helper functions that facilitate passing data to the dispatcher.

All data flows through the dispatcher as a central hub. Actions most often originate from user interactions with the views, and are nothing more than a call into the dispatcher. The dispatcher then invokes the callbacks that the stores have registered with it, effectively dispatching the data payload contained in the actions to all stores. Within their registered callbacks, stores determine which actions they are interested in, and respond accordingly. The stores then emit a "change" event to alert the controller-views that a change to the data layer has occurred. Controller-views listen for these events and retrieve data from the stores in an event handler. The controller-views call their own render() method via setState() or forceUpdate(), updating themselves and all of their children.

This structure allows us to reason easily about our application in a way that is reminiscent of functional reactive programming, or more specifically data-flow programming or flow-based programming, where data flows through the application in a single direction โ€” there are no two-way bindings. Application state is maintained only in the stores, allowing the different parts of the application to remain highly decoupled. Where dependencies do occur between stores, they are kept in a strict hierarchy, with synchronous updates managed by the dispatcher.

We found that two-way data bindings led to cascading updates, where changing one object led to another object changing, which could also trigger more updates. As applications grew, these cascading updates made it very difficult to predict what would change as the result of one user interaction. When updates can only change data within a single round, the system as a whole becomes more predictable.

Let's look at the various parts of the Flux update cycle up close. A good place to start is the dispatcher.

A Single Dispatcher

The dispatcher is the central hub that manages all data flow in a Flux application. It is essentially a registry of callbacks into the stores. Each store registers itself and provides a callback. When the dispatcher responds to an action, all stores in the application are sent the data payload provided by the action via the callbacks in the registry.

As an application grows, the dispatcher becomes more vital, as it can manage dependencies between stores by invoking the registered callbacks in a specific order. Stores can declaratively wait for other stores to finish updating, and then update themselves accordingly.

Stores

Stores contain the application state and logic. Their role is somewhat similar to a model in a traditional MVC, but they manage the state of many objects โ€” they are not instances of one object. Nor are they the same as Backbone's collections. More than simply managing a collection of ORM-style objects, stores manage the application state for a particular domain within the application.

For example, Facebook's Lookback Video Editor utilized a TimeStore that kept track of the playback time position and the playback state. On the other hand, the same application's ImageStore kept track of a collection of images. The TodoStore in our TodoMVC example is similar in that it manages a collection of to-do items. A store exhibits characteristics of both a collection of models and a singleton model of a logical domain.

As mentioned above, a store registers itself with the dispatcher and provides it with a callback. This callback receives the action's data payload as a parameter. The payload contains a type attribute, identifying the action's type. Within the store's registered callback, a switch statement based on the action's type is used to interpret the payload and to provide the proper hooks into the store's internal methods. This allows an action to result in an update to the state of the store, via the dispatcher. After the stores are updated, they broadcast an event declaring that their state has changed, so the views may query the new state and update themselves.

Views and Controller-Views

React provides the kind of composable views we need for the view layer. Close to the top of the nested view hierarchy, a special kind of view listens for events that are broadcast by the stores that it depends on. One could call this a controller-view, as it provides the glue code to get the data from the stores and to pass this data down the chain of its descendants. We might have one of these controller-views governing any significant section of the page.

When it receives the event from the store, it first requests the new data it needs via the stores' public getter methods. It then calls its own setState() or forceUpdate() methods, causing its render() method and the render() method of all its descendants to run.

We often pass the entire state of the store down the chain of views in a single object, allowing different descendants to use what they need. In addition to keeping the controller-like behavior at the top of the hierarchy, and thus keeping our descendant views as functionally pure as possible, passing down the entire state of the store in a single object also has the effect of reducing the number of props we need to manage.

Occasionally we may need to add additional controller-views deeper in the hierarchy to keep components simple. This might help us to better encapsulate a section of the hierarchy related to a specific data domain. Be aware, however, that controller-views deeper in the hierarchy can violate the singular flow of data by introducing a new, potentially conflicting entry point for the data flow. In making the decision of whether to add a deep controller-view, balance the gain of simpler components against the complexity of multiple data updates flowing into the hierarchy at different points. These multiple data updates can lead to odd effects, with React's render method getting invoked repeatedly by updates from different controller-views, potentially increasing the difficulty of debugging.

Actions

The dispatcher exposes a method that allows a view to trigger a dispatch to the stores, and to include a payload of data, or an action. The action construction may be wrapped into a semantic helper method which sends the payload to the dispatcher. For example, we may want to change the text of a to-do item in a to-do list application. We would create an action with a function signature like updateText(todoId, newText) in our TodoActions module. This method may be invoked from within our views' event handlers, so we can call it in response to a user action. This action method also adds the action type to the payload, so that when the payload is interpreted in the store, it can respond appropriately to a payload with a particular action type. In our example, this type might be named something like TODO_UPDATE_TEXT.

Actions may also come from other places, such as the server. This happens, for example, during data initialization. It may also happen when the server returns an error code or when the server has updates to provide to the application. We'll talk more about server actions in a future article. In this post we're only concerned with the basics of the data flow.

What About that Dispatcher?

As mentioned earlier, the dispatcher is also able to manage dependencies between stores. This functionality is available through the waitFor() method within the Dispatcher class. The TodoMVC application is extremely simple, so we did not need to use this method, but we have included it here as an example of what a dispatcher should be able to do in a larger, more complex application.

Within the TodoStore's registered callback we can explicitly wait for any dependencies to first update before moving forward:

case 'TODO_CREATE': 
  Dispatcher.waitFor([ 
    PrependedTextStore.dispatcherIndex, 
    YetAnotherStore.dispatcherIndex 
  ], function() { 
    TodoStore.create(PrependedTextStore.getText() + ' ' + action.text); 
    TodoStore.emit('change'); 
  }); 
  break; 

The arguments for waitFor() are an array of dipatcher registry indexes, and a final callback to invoke after the callbacks at the given indexes have completed. Thus the store that is invoking waitFor() can depend on the state of another store to inform how it should update its own state.

A problem arises if we create circular dependencies. If Store A waits for Store B, and B waits for A, then we'll have a very bad situation on our hands. We'll need a more robust dispatcher that flags these circular dependencies with console errors, and this is not easily accomplished with promises. Unfortunately, that's a bit beyond the scope of this documentation. In future blog posts, we hope to cover how to build a more robust dispatcher and how to initialize, update, and save the state of the application with persistent data, like a web service API.

TodoMVC Example Implementation

In this TodoMVC example application, we can see the elements of Flux in our directory structure. Views here are referred to as "components" as they are React components.

./
  index.html
  js/
    actions/
      TodoActions.js
    app.js
    bundle.js
    dispatcher/
      AppDispatcher.js
      Dispatcher.js
    components/
      Footer.react.js
      Header.react.js
      MainSection.react.js
      TodoApp.react.js
      TodoItem.react.js
      TodoTextInput.react.js
    stores/
      TodoStore.js

The primary entry point into the application is app.js. This file bootstraps the React rendering inside of index.html. TodoApp.js is our controller-view and it passes all data down into its child React components.

TodoActions.js is a collection of actions that views may call from within their event handlers, in response to user interactions. They are nothing more than helpers that call into the AppDispatcher.

Dispatcher.js is a base class for AppDispatcher.js which extends it with a small amount of application-specific code. This dispatcher is a naive implementation based on promises, but much more robust implementations are possible.

TodoStore.js is our only store. It provides all of the application logic and in-memory storage. Based on EventEmitter from Node.js, it emits "change" events after responding to actions in the callback it registers with the dispatcher.

The bundle.js file is automatically genenerated by the build process, explained below.

Running

You must have npm installed on your computer. From the root project directory run these commands from the command line:

npm install

This will install all dependencies.

To build the project, first run this command:

npm start

This will perform an initial build and start a watcher process that will update build.js with any changes you wish to make. This watcher is based on Browserify and Watchify, and it transforms React's JSX syntax into standard JavaScript with Reactify.

To run the app, spin up an HTTP server and visit http://localhost/.../todomvc-flux/.

Credit

This TodoMVC application was created by Bill Fisher. This README document was written by Bill Fisher and the principal creator of Flux, Jing Chen.

License

Copyright 2013-2014 Facebook, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

More Repositories

1

autobind-decorator

Decorator to automatically bind methods to class instances
JavaScript
1,448
star
2

reactify

[DEPRECATED] Browserify transform for JSX (superset of JavaScript used in React library by Facebook)
JavaScript
689
star
3

react-css-components

Define React presentational components with CSS
JavaScript
676
star
4

react-fa

DEPRECATED: use https://github.com/FortAwesome/react-fontawesome instead
JavaScript
492
star
5

react-async

[DEPRECATED] Asynchronously fetch data for React components
JavaScript
446
star
6

react-quickstart

[DEPRECATED] React project template with server-side UI rendering and routing
JavaScript
370
star
7

react-time

Component for React to render relative and/or formatted dates into <time> HTML5 element
JavaScript
211
star
8

styling

Create CSS modules with the full power of JavaScript
JavaScript
130
star
9

react-derivable

React bindings for derivable state computation library
JavaScript
122
star
10

sitegen

Generate websites by composing React components
JavaScript
118
star
11

reactdown

Markdown based live document format
JavaScript
116
star
12

rrouter

Declarative routing layer for React applications
JavaScript
114
star
13

backbone.projections

backbone.projections is a set of projections for Backbone.Collection
CoffeeScript
110
star
14

validated

Validate your configurations with precise error messages
JavaScript
91
star
15

typescript-loader

[DEPRECATED] TypeScript Webpack Plugin
JavaScript
82
star
16

react-flexgrid

Flexbox Grid reimagined as a set of React components
JavaScript
73
star
17

less2stylus

[NOT MAINTAINED] LESS to Stylus source to source convertor capable of translating Bootstrap
CoffeeScript
71
star
18

xcss

xCSS is a library for programmatic stylesheet composition
JavaScript
63
star
19

backbone.viewdsl

Declarative view technology for Backbone
JavaScript
40
star
20

react-stylesheet

[DEPRECATED] A component for React to declare stylesheet dependencies for your reusable components
JavaScript
40
star
21

cssobjectify

Browserify transform to turn stylesheets into JSON objects
JavaScript
39
star
22

es6-template-strings-jsx

JavaScript
38
star
23

rethemeable

Utilities for producing and consuming themable React components
JavaScript
35
star
24

react-app-express

*DEPRECATED* React + Express + Browserify + History API + Server Side Rendering
JavaScript
32
star
25

react-app-controller

*DEPRECATED* React application controller to manage top-level React components according to window.location
JavaScript
32
star
26

type-systems

Playing with type systems
OCaml
32
star
27

routr

Request routing for WebOb based WSGI applications
Python
29
star
28

memoize-decorator

Memoize getters and methods to compute only once
JavaScript
29
star
29

sweet-assertions

Syntax for writing informative testing assertions
JavaScript
29
star
30

sweetify

Browserify transform for using Sweet.js macros
JavaScript
29
star
31

upaas

ฮผPaaS โ€” nano PaaS based on Docker and gitreceive
Shell
28
star
32

rescript

[PoC] Rescript is a scripting runtime for ReasonML
OCaml
25
star
33

webpack-package-loaders-plugin

Webpack module loaders discovery through package.json
JavaScript
25
star
34

julia-repl-vim

Julia REPL plugin for vim/neovim
Julia
24
star
35

react-app

*DEPRECATED* Rapid appliaction development with React
JavaScript
23
star
36

ctags-webpack-plugin

Webpack plugin to generate accurate ctags
JavaScript
21
star
37

purescript-node-thunk

Node callbacks as thunks
PureScript
20
star
38

console-ui

Composable console output (somewhat inspired by React)
JavaScript
20
star
39

rrun

[WIP] rrun allows to seamlessly run Reason/OCaml code with native speed
OCaml
19
star
40

sweet-jsx

Use JSX and sweet.js macros together
JavaScript
18
star
41

ppx_let_promise

Like async/await syntax for Promises in JS but for OCaml
OCaml
18
star
42

K.jl

K programming language dialect embedded in Julia
Julia
17
star
43

jsonpublish

Configurable JSON encoder for publishing Python objects as JSON documents
Python
17
star
44

markstruct

Block-based structured editor for Markdown
JavaScript
16
star
45

es6-module-jstransform

ES6 module syntax to CommonJS transformation
JavaScript
16
star
46

BQN.jl

BQN implementation in Julia
Julia
16
star
47

vim-flow-outline

Outline for JS modules with Flow
Vim Script
16
star
48

rework-macro

Macro CSS transform for rework/xcss
JavaScript
16
star
49

esy-docker

A set of make rules to produce docker images for esy projects
Makefile
15
star
50

configure

Configuration toolkit based on YAML
Python
15
star
51

mocha-doctest

Test your documentation
JavaScript
14
star
52

connect-browserify

Connect/express middelware for serving front-end applications with browserify.
JavaScript
13
star
53

YouTubeManager

YouTubeManager is an wrapper for YouTube JS Player API which tries to mimic SoundManager2 API.
CoffeeScript
13
star
54

domain-context

Globally accessible domain-bound contexts, connect/express middleware included
CoffeeScript
12
star
55

babel-plugin-ast-literal

Babel Plugin AST Literal
JavaScript
11
star
56

react-custom-events

Don't use this, this was an experiment and it doesn't work anymore with recent versions of React
JavaScript
11
star
57

react-image-size-loader

Webpack loader for images which turns them into <img /> components with height and width
JavaScript
11
star
58

wpack

JavaScript
10
star
59

jstransformify

Browserify transform which applies jstransform visitors
JavaScript
9
star
60

react-macros

A set of syntax extensions for React
JavaScript
8
star
61

backbone.viewevents

Events for Backbone.View which can bubble up through view hierarchy.
JavaScript
7
star
62

prefetch-context-webpack-plugin

Webpack plugin which prefetches context (all files within the directory tested by a regular expression)
JavaScript
7
star
63

musvox

Collaborative music listening environment for Minecraft-like voxel worlds
JavaScript
7
star
64

lang-julia

Julia language support for the CodeMirror code editor
TypeScript
7
star
65

react-dom-events

**DO NOT USE THIS**
JavaScript
7
star
66

sphinx-npm

Sphinx documentation tool launcher which builds docs for npm packages
JavaScript
6
star
67

dream-totp-auth

An example Dream app with password auth & totp
OCaml
6
star
68

extracty

a set of tools to extract metadata from HTML documents (WIP)
Python
6
star
69

bw_sphinxtheme

Sphinx theme in black and white colours.
JavaScript
6
star
70

inets_mod_proxy

Simple HTTP proxy module for erlang inets httpd service.
Erlang
6
star
71

swarm-react

JavaScript
5
star
72

esy-bsb-example

OCaml
5
star
73

stream-rpc

RPC over arbitrary streams for Node.js and a browser
CoffeeScript
5
star
74

require-assets

A library to package and re-use static assets
JavaScript
5
star
75

asyncomplete-ale.vim

LSP completion source (via ALE) for asyncomplete.vim
Vim Script
5
star
76

react-router-component-bower

Bower package for react-router-component
JavaScript
5
star
77

deps-topo-sort

Sort module-deps/dgraph output topologically
JavaScript
5
star
78

fzf-merlin

Vim Script
5
star
79

diffbot

DiffBot API wrapper (uses urllib3)
Python
5
star
80

purescript-immutable

PureScript bindings to Immutable.js library
PureScript
5
star
81

contentlet

Framework for creating composable and reusable web UI.
Python
5
star
82

aoc2021

AOC2021 in BQN
5
star
83

webpack-stylegen

JavaScript
5
star
84

backbone.module

Spine.Module but for Backbone
CoffeeScript
4
star
85

dgraph

Build and transform dependency graphs from JS, CSS or other code bases
JavaScript
4
star
86

sphinxalchemy

SphinxQL dialect for SQLAlchemy (uses MySQLdb-Python for wire protocol)
Python
4
star
87

es6-destructuring-jstransform

ES6 destructuring assignment and destructuring function arguments transformation.
JavaScript
4
star
88

reason-react-workshop

CSS
4
star
89

wall

Extremely hackable HN/Reddit clone in PostgreSQL + Node + Express + React
CoffeeScript
4
star
90

esy-solve-cudf

Makefile
4
star
91

esy-reason-graphql-server

[EXAMPLE REPO, NOT MAINTAINED] Reason + GraphQL on esy
OCaml
4
star
92

faviconr

Fast and robust favicon resolution
CoffeeScript
4
star
93

jsxx

JSX eXperimental
JavaScript
4
star
94

ipsql

Intelligent PostgreSQL shell (concept)
Python
3
star
95

pureact

PureScript
3
star
96

ppx_router

type safe routing for Dream
OCaml
3
star
97

docgen.mk

a set of utilities and make macros for static site generation
Python
3
star
98

fb.py

Python bindings for Facebook Graph API
Python
3
star
99

react-async-middleware

Connect/express middleware to serve react-async components
JavaScript
3
star
100

react-pad

Authoring tool for React components
JavaScript
3
star