UNPKG

fluorine-lib

Version:

Reactive state and side effect management for React using a single stream of actions

149 lines (110 loc) 4.39 kB
import { Subject } from 'rxjs/Subject'; import { Observable } from 'rxjs/Observable'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { of } from 'rxjs/observable/of'; import { concat } from 'rxjs/operator/concat'; import { map } from 'rxjs/operator/map'; import { mergeMap } from 'rxjs/operator/mergeMap'; import { distinctUntilChanged } from 'rxjs/operator/distinctUntilChanged'; import { publishReplay } from 'rxjs/operator/publishReplay'; import { subscribeOn } from 'rxjs/operator/subscribeOn'; import { share } from 'rxjs/operator/share'; import { filter } from 'rxjs/operator/filter'; import { _catch } from 'rxjs/operator/catch'; import { createState, filterActions } from './util/state'; import { parseOpts, logAgendas, logStore } from './util/logger'; import assert from './util/assert'; import wrapActions from './util/wrapActions'; import toObservable from './util/toObservable'; import isObservable from './util/isObservable'; var KICKSTART_ACTION = { type: '_INIT_' }; export function Dispatcher() { var _this = this; var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var middlewares = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; Subject.call(this); this.keyCache = []; this.valCache = []; this.middlewares = [].concat(middlewares).map(function (x) { return x(_this); }); // Options: Logging this.logging = parseOpts(opts.logging); if (this.logging.agendas) { logAgendas(this); } this.reduce = this.reduce.bind(this); this.rawNext = this.rawNext.bind(this); this.next = this.next.bind(this); } // Inherit from Rx.Subject Dispatcher.prototype = Object.create(Subject.prototype); Dispatcher.prototype.constructor = Dispatcher; Dispatcher.prototype.reduce = function reduce(fn, init) { var _context; var keyCache = this.keyCache, valCache = this.valCache, logging = this.logging; var index = keyCache.indexOf(fn); if (index > -1) { return valCache[index].store; } // Create cursor pointing to the state history var cursor = createState(fn, fn(init, KICKSTART_ACTION)); // Describe states using the series of agendas var store = (_context = (_context = (_context = of(cursor.state), concat).call(_context, mergeMap.call(this, function (agenda) { var _context2; // Reference agenda's root state var anchor = cursor; // Collect agenda's actions var actions = []; // Prepare agenda logger if necessary var logger = logging.stores ? logStore(fn.name || index, agenda) : null; // Map Agenda to consecutive states and catch errors return (_context2 = (_context2 = map.call(agenda, function (action) { cursor = cursor.doNext(action); actions.push(action); if (logger) { logger.change(action, cursor.state); // Logging new state by action } return cursor.state; }), _catch).call(_context2, function (err) { if (!logger) { console.error(err); } // Filter past actions by all of the failed agenda var previousState = cursor.state; filterActions(anchor, function (x) { return actions.indexOf(x) === -1; }); if (logger) { logger.revert([previousState, cursor.state], err, actions); // Logging reversion } return of(cursor.state); }), distinctUntilChanged).call(_context2); })), distinctUntilChanged).call(_context), publishReplay).call(_context, 1); var subscription = store.connect(); // Cache the store var key = keyCache.length; keyCache.push(fn); valCache[key] = { store: store, subscription: subscription }; return store; }; // Save subject's normal next method Dispatcher.prototype.rawNext = Dispatcher.prototype.next; Dispatcher.prototype.next = function next(arg) { var _context3; var middlewares = this.middlewares; var agenda = (_context3 = toObservable(arg), share).call(_context3); for (var i = 0; i < middlewares.length; i++) { var middleware = middlewares[i]; agenda = middleware(agenda); if (!isObservable(agenda)) { return undefined; } } return this.rawNext((_context3 = (_context3 = agenda, filter).call(_context3, Boolean), publishReplay).call(_context3).refCount()); }; export default function createDispatcher(opts, middlewares) { return new Dispatcher(opts, middlewares); }