Skip to content

Instantly share code, notes, and snippets.

Last active August 29, 2015 14:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acdlite/c7eb7ac45617d9332b60 to your computer and use it in GitHub Desktop.
Save acdlite/c7eb7ac45617d9332b60 to your computer and use it in GitHub Desktop.
Imagining a more functional, classless Flummox API
import Flummox from 'flummox';
import { Map } from 'immutable';
// Instead of a constructor, just wrap flux creation inside a function
export default function createFlux() {
const flux = new Flummox();
// Actions are the same. Just pass an object instead of a class.
const thingActions = flux.createActions('things', {
incrementSomething(amount) {
return amount;
async createThing(newThing) {
// async/await isn't strictly necsesary here -- we could just return the
// the promise, but I like the reminder that this is async
return await WebAPIUtils.createThing(newThing);
const thingActionIds = fooActions.getActionIds();
// Note that everything above is already possible in the current version
// of Flummox.
// Here's where things get interesting...
const thingStore = flux.createStore('things', {
getInitialState: () => ({
things: new Map(),
_pendingThings: new Map(),
counter: 0
// Instead of `this.register()` et al, use hooks (like React's lifecycle
// hooks) to set up action handlers. `register()` should return a hash of
// action ids mapped to handlers.
register: () => ({
// Return new store state, given the previous state and the value sent by
// the action. Like with `setState()`, the new state is merged with the
// old state. It's like a transactional state update, or a
// reduce operation (except it's a merge, not a replace).
// All params are passed as one hash, to support possible future interop
// with RxJS. Yay for destructuring!
[thingActionIds.incrementSomething]: ({ prevState, value: amount }) => ({
counter: prevState.counter + amount
// For async actions, use a sub-hash with success and failure handlers.
// The naming convention is taken from RxJS observers. Notice that there's
// no `onBegin`, which you might expect if you're used to
// Flummox's `registerAsync()`. For that, use the `registerOnStart()` hook
[thingActionIds.createThing]: {
onNext: ({ prevState, value: newThing, payload: { dispatchId } }) => ({
things: prevState.things.merge({
[]: newThing
_pendingThings: prevState.delete(dispatchId)
onError: ({ payload: { dispatchId } }) => ({
_pendingThings: prevState.delete(dispatchId)
// Specify handlers that fire at the beginning async actions, in order to
// perform optimistic updates
registerOnStart: () => ({
[thingActionIds.createThing]: ({ prevState, payload: { dispatchId, actionArgs: [ newThing ] }}) => ({
_pendingThings: prevState._pendingThings.merge({
[dispatchId]: newThing
// We could support both the class-based API and this new functional API
// without much fuss. I think this is important, since much of the appeal of
// Flummox comes from the familiarity and predictability of its API. Many
// people are not used to functional programming concepts, and will want to
// stick with classes.
Copy link

I do understand why you want to separate them out into two sections, but just as for tappleby it feels a bit off for me. His syntax would allow us to at least keep things a bit closer together. How about something like this:

  register: () => ({
    [thingActionIds.createThing]: {
      begin() {...},
      stream: {onNext, onError}

But now that I've written that out, it feels weird having to nest the stuff like that. I'll leave it here for discussion anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment