NuclearJS

Reactive Flux built with ImmutableJS data structures.

Simple & Elegant Flux

Singular application state

All application state is stored in one Immutable Map, similar to Om.

Stores declaratively register pure functions to handle state changes, massively simplifying testing and debugging state changes.

Powerful functional dataflow

Compose and transform your data together statelessly and efficiently using a functional lens concept called Getters.

This allows your views to receive exactly the data they need in a way that is fully decoupled from stores. Best of all, this pattern eliminates the confusing store.waitsFor method found in other Flux implementations.

Reactive

Any Getter can be observed by a view to be notified whenever its derived value changes.

NuclearJS includes tools to integrate with libraries such as React and VueJS out of the box.

Efficient

Thanks to immutable data, change detection can be efficiently performed at any level of granularity by a constant time reference equality (===) check.

Since Getters use pure functions, NuclearJS utilizes memoization to only recompute parts of the dataflow that might change.

Filter by type:
NameTypePrice
bananafood1
doritosfood4
shirtclothes15
pantsclothes20
User action updates application state
AppState {
  "typeFilter": null,
  "items": [
    {"type": "food","name": "banana","price": 1},
    {"type": "food","name": "doritos","price": 4},
    {"type": "clothes","name": "shirt","price": 15},
    {"type": "clothes","name": "pants","price": 20}
  ]
}
Getters compose and transform application state reactively notifying components of any changes.
filteredItems Getter [
  {"type": "food","name": "banana","price": 1},
  {"type": "food","name": "doritos","price": 4},
  {"type": "clothes","name": "shirt","price": 15},
  {"type": "clothes","name": "pants","price": 20}
]

Usage:

import { Reactor, Store, toImmutable } from 'nuclear-js'
import React from 'react'

const reactor = new Reactor({ debug: true });

reactor.registerStores({
  typeFilter: Store({
    getInitialState() {
      return null;
    },

    initialize() {
      this.on('FILTER_TYPE', (state, type) => type)
    }
  }),

  items: Store({
    getInitialState() {
      return toImmutable([
        { type: 'food', name: 'banana', price: 1 },
        { type: 'food', name: 'doritos', price: 4 },
        { type: 'clothes', name: 'shirt', price: 15 },
        { type: 'clothes', name: 'pants', price: 20 },
      ])
    },

    initialize() {
      this.on('ADD_ITEM', (state, item) => state.push(item))
    }
  })
})

Create a Reactor

In NuclearJS the reactor acts as the dispatcher, maintains the application state and provides an API for data access and observation.

Register stores

Stores determine the shape of your application state. Stores define two methods:

getInitialState() - Returns the initial state for that stores specific key in the application state.

initialize() - Sets up any action handlers, by specifying the action type and a function that transforms

(storeState, action) => (newStoreState)

const filteredItemsGetter = [
  ['typeFilter'],
  ['items'],
  (filter, items) => {
    return (filter)
      ? items.filter(i => i.get('type') === filter)
      : items
  }
]

Accessing your data

Getters allow you to easily compose and transform your application state in a reusable way.

const ItemViewer = React.createClass({
  mixins: [reactor.ReactMixin],

  getDataBindings() {
    return {
      items: filteredItemsGetter
    }
  },

  render() {
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Type</th>
            <th>Price</th>
          </tr>
        </thead>

        <tbody>
          {this.state.items.map(item => {
            return <tr>
              <td>{item.get('name')}</td>
              <td>{item.get('type')}</td>
              <td>{item.get('price')}</td>
            </tr>
          })}
        </tbody>
      </table>
    )
  }
})

Automatic component data binding

Simply use the reactor.ReactMixin and implement the getDataBindings() function to automatically sync any getter to a this.state property on a React component.

Since application state can only change after a dispatch then NuclearJS can be intelligent and only call this.setState whenever the actual value of the getter changes. Meaning less pressure on React's DOM diffing.

Framework agnostic

This example shows how to use NuclearJS with React, however the same concepts can be extended to any reactive UI framework. In fact, the ReactMixin code is only about 40 lines.

const actions = {
  setFilter(type) {
    reactor.dispatch('FILTER_TYPE' type)
  },

  addItem(name, type, price) {
    reactor.dispatch('ADD_ITEM', toImmutable({
      name,
      type,
      price
    }))
  }
}

actions.addItem('computer', 'electronics', 1999)
actions.setFilter('electronics')

Dispatching actions

NuclearJS maintains a very non-magical approach to dispatching actions. Simply call reactor.dispatch with the actionType and payload.

All action handling is done synchronously, leaving the state of the system very predictable after every action.

Because actions are simply functions, it is very easy to compose actions together using plain JavaScript.

// Evaluate by key path
var itemsList = reactor.evaluate(['items'])
var item0Price = reactor.evaluate(['items', 0, 'price'])

// Evaluate by getter
var filteredItems = reactor.evaluate(filteredItemsGetter)

// Evaluate and coerce to plain JavaScript
var itemsPOJO = reactor.evaluateToJS(filteredItemsGetter)

// Observation
reactor.observe(filteredItemsGetter, items => {
  console.log(items)
})

Reading application state

NuclearJS also provides imperative mechanisms for evaluating and observing state.

In fact any getter can synchronously and imperatively evaluated or observed. The entire ReactMixin is built using only those two functions.