<commit_before># React+Bacon.js TodoMVC

Demonstration about the power and simplicity of React/FRP combo.

## Motivation

Flux does a nice job when abstracting views from the "business logic".
However Flux application tend to be complex and have a slight enterprise
smell with all those actions dispatchers, stores and listeners. This
introduces a lot of boilerplate and unnecessary complexity to the 
codebase. 

This TodoMVC project demonstrates how to bypass all those intermediate 
steps and keep your views clean with the power of FRP.

## How it works?

The most fundamental concept of [Functional Reactive Programming (FRP)](http://en.wikipedia.org/wiki/Functional_reactive_programming)
is the **event stream**. Streams are like arrays of events: they can be mapped, 
filtered, merged and combined. 

The **main idea** is that every user action is just an event stream that is merged 
to the "application state" stream. Occurred events cause the application state to 
change. Finally the changed state object is then rendered **at the top level** of
the application: React's virtual DOM does the rest!

This enables **extremely simple** React views. No callbacks and/or listener 
registrations are needed: the views only render what they are given and
call synchronously the business logic interface on user actions. Business logic
streams then propagate the state change back to views (which is again rendered
stupidly). Nothing more is needed!

![Application architecture](doc/diagram.png)

The essential component is the `Dispatcher` which is basically just an object
of Bacon buses (`Bacon.Bus` is a stream that can have data pushed into it).

```javascript
const Bacon = require('baconjs')

module.exports = function() {
  const busCache = {}

  this.stream = function(name) {
    return bus(name)
  }
  this.push = function(name, value) {
    bus(name).push(value)
  }
  this.plug = function(name, value) {
    bus(name).plug(value)
  }

  function bus(name) {
    return busCache[name] = busCache[name] || new Bacon.Bus()
  }
}
```

This dispatcher enables the easy emitting and listening of user actions:

```javascript
const Bacon       = require('baconjs'),
      Dispatcher  = require('./dispatcher')

const d = new Dispatcher()

module.exports = {
  toItemsProperty: function(initialItems, filterS) {
    const itemsS = Bacon.update(initialItems,
      [d.stream('remove')],           removeItem,
      [d.stream('create')],           createItem,
      [d.stream('addState')],         addItemState,
      [d.stream('removeState')],      removeItemState,
      [d.stream('removeCompleted')],  removeCompleteItems,
      [d.stream('updateTitle')],      updateItemTitle
    )

    return Bacon.combineAsArray([itemsS, filterS])
      .map(withDisplayStatus)

    ... business logic here ...
  },

  // "public" methods

  createItem: function(title) {
    d.push('create', title)
  },

  removeItem: function(itemId) {
    d.push('remove', itemId)
  },

  removeCompleted: function() {
    d.push('removeCompleted')
  },

  setTitle: function(itemId, title) {
    d.push('updateTitle', {itemId, title})
  },
  
  ...
}
```

After that, using your business logic is dead simple:

```jsx
const todos = require('./todos')
...<commit_msg>Update docs<commit_after># React+Bacon.js TodoMVC

Demonstration about the power and simplicity of React/FRP combo.

## Motivation

Flux does a nice job when abstracting views from the "business logic".
However Flux application tend to be complex and have a slight enterprise
smell with all those actions dispatchers, stores and listeners. This
introduces a lot of boilerplate and unnecessary complexity to the 
codebase. 

This TodoMVC project demonstrates how to bypass all those intermediate 
steps and keep your views clean with the power of FRP.

## How it works?

The most fundamental concept of [Functional Reactive Programming (FRP)](http://en.wikipedia.org/wiki/Functional_reactive_programming)
is the **event stream**. Streams are like arrays of events: they can be mapped, 
filtered, merged and combined. 

The **main idea** is that every user action is just an event stream that is merged 
to the "application state" stream. Occurred events cause the application state to 
change. Finally the changed state object is then rendered **at the top level** of
the application: React's virtual DOM does the rest!

This enables **extremely simple** React views. No callbacks and/or listener 
registrations are needed: the views only render what they are given and
call synchronously the business logic interface on user actions. Business logic
streams then propagate the state change back to views (which is again rendered
stupidly). Nothing more is needed!

![Application architecture](doc/diagram.png)

The essential component is the `Dispatcher` which is basically just an object
of Bacon buses (`Bacon.Bus` is a stream that can have data pushed into it. Equals Rx's `Subject`).

```javascript
const Bacon = require('baconjs')

module.exports = function() {
  const busCache = {}

  this.stream = function(name) {
    return bus(name)
  }
  this.push = function(name, value) {
    bus(name).push(value)
  }
  this.plug = function(name, value) {
    bus(name).plug(value)
  }

  function bus(name) {
    return busCache[name] = busCache[name] || new Bacon.Bus()
  }
}
```

This dispatcher enables the easy emitting and listening of user actions:

```javascript
const Bacon       = require('baconjs'),
      Dispatcher  = require('./dispatcher')

const d = new Dispatcher()

module.exports = {
  toItemsProperty: function(initialItems, filterS) {
    const itemsS = Bacon.update(initialItems,
      [d.stream('remove')],           removeItem,
      [d.stream('create')],           createItem,
      [d.stream('addState')],         addItemState,
      [d.stream('removeState')],      removeItemState,
      [d.stream('removeCompleted')],  removeCompleteItems,
      [d.stream('updateTitle')],      updateItemTitle
    )

    return Bacon.combineAsArray([itemsS, filterS])
      .map(withDisplayStatus)

    ... business logic here ...
  },

  // "public" methods

  createItem: function(title) {
    d.push('create', title)
  },

  removeItem: function(itemId) {
    d.push('remove', itemId)
  },

  removeCompleted: function() {
    d.push('removeCompleted')
  },

  setTitle: function(itemId, title) {
    d.push('updateTitle', {itemId, title})
  },
  
  ...
}
```

Business logic can be added to state stream easily with `Bacon.combineTemplate`:

```javascript
const React   = require('react'),
      Bacon   = require('baconjs'),
      TodoApp = require('./todoApp'),
      todos   = require('./todos'),
      filter  = require('./filter')

const filterP = filter.toProperty(...intial filter state...),
      itemsP  = todos.toItemsProperty([], filterP)

const appState = Bacon.combineTemplate({
  items: itemsP,
  filter: filterP
})

appState.onValue((state) => {
  React.render(<TodoApp {...state} />, document.getElementById('todoapp'))
})
```

After that, using your business logic is dead simple:

```jsx
const todos = require('./todos')
...
{'fraction_non_alphanumeric': 0.10724841660802252, 'fraction_numerical': 0.0, 'mean_word_length': 3.781965006729475, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7598961', 'n_tokens_mistral': 2082, 'n_tokens_neox': 1983, 'n_words': 734}