Giter VIP home page Giter VIP logo

alt's Introduction

alt

Check out the API Reference for full in-depth docs. For a high-level walk-through on flux, take a look at the Getting Started guide. What follows below applies only to the master branch of alt and not the latest distribution. Any questions? ask in the gitter room.

Gitter

NPM version Build Status Coverage Status Dependency Status Download Count JS.ORG

Why you should be using Alt

  • It is pure flux. Stores have no setters, the flow is unidirectional.
  • Isomorphic and works with react-native.
  • Actively maintained and being used in production.
  • Extremely flexible and unopinionated in how you use flux. Create traditional singletons or use dependency injection.
  • It is terse. No boilerplate.

What does it look like?

Alt

import Alt from 'alt';
export default new Alt();

Actions

import alt from './alt';

class TodoActions {
  updateTodo(id, text) {
    return { id, text }
  }
}

export default alt.createActions(TodoActions);

Store

import alt from './alt';
import TodoActions from './TodoActions'

class TodoStore {
  constructor() {
    this.bindListeners({
      updateTodo: TodoActions.updateTodo
    });

    this.state = {
      todos: []
    };
  }

  updateTodo(todo) {
    this.setState({ todos: this.state.todos.concat(todo) });
  }
}

export default alt.createStore(TodoStore, 'TodoStore');

View

Using the connectToStores util from alt-utils package (npm install alt-utils)

// ES2015 (ES6)
import connectToStores from 'alt-utils/lib/connectToStores';
import { Component } from 'react';
import TodoStore from './TodoStore';

class TodoView extends Component {
  static getStores() {
    return [TodoStore];
  }

  static getPropsFromStores() {
    return TodoStore.getState();
  }

  render() {
    return (
      <ul>
        {this.props.todos.map((todo) => {
          return (
            <li key={todo.id}>{todo.text}</li>
          );
        })}
      </ul>
    );
  }
}
export default connectToStores(TodoView);

or

//ES2016 (ES7) using @connectToStores Decorator
import connectToStores from 'alt-utils/lib/connectToStores';
import { Component } from 'react';
import TodoStore from './TodoStore';

@connectToStores
class TodoView extends Component {
  static getStores() {
    return [TodoStore];
  }

  static getPropsFromStores() {
    return TodoStore.getState();
  }

  ...
}

In the Wild

Examples

Boilerplates

Pure Flux + More

  • Unidirectional data flow
  • Stores have no setters
  • Inversion of control
  • Single central dispatcher
  • All Stores receive the dispatch

Read about the Principles of Flux.

One really cool aspect of alt is that you can save snapshots of the entire application's state at any given point in time. This has many different use cases like:

  • Time traveling through the state of your application. For fun and profit.
  • Being able to debug from a broken state. Have your team send you the exact state the app was in when it crashed.
  • Isomorphism. You save a snapshot that you send from the server to the client and then bootstrap back on the client.
  • Rolling back to previous stable states.

There are also many utils available which interface well with alt:

  • ActionListener lets you listen to individual actions without having to create a store.
  • AltContainer a higher-order container component that is your swiss army knife for React.
  • AltIso addon that uses iso to render your application on both server and client.
  • atomic enables your stores for atomic transactions.
  • connectToStores a higher-order function that wraps your React components for store listening.
  • decorators a collection of useful ES7 decorators for working with alt.
  • DispatchRecorder lets you record all your dispatches and replay them back at a later time.
  • FinalStore is a Store that you can listen to that only emits when all your other stores have received all their data.
  • ImmutableUtil makes working with immutable-js easy.
  • TimeTravel enhances your stores so they are able to travel through different states in time.

Topical Guide

First we install alt through npm. Although alt is also available through bower.

npm install alt

The following topical guide covers on using alt as a singleton in a traditional flux way.

We'll be referring back to this code a lot by using the alt reference declared.

const Alt = require('alt');
const alt = new Alt();

ES6

Alt is written in, and encourages ES6. It is completely optional but it is pleasant to write.

You can use the es6 transpiler that comes with react courtesy of jstransform or you can use one of the other popular ES6 transpilers: babel or traceur.

You won't need an es6-shim but you can use one for further goodies in your javascripts.

Alt does depend on ES5 features, the good news is so does React. You can use es5-shim to support those pesky old browsers.

##Typescript Definitions and Support The typescript definitions for alt are located in the typings directory. This should be included in your project under typings/alt or whatever folder you use to manage your definitions files. You can import the dependencies react and es6-promises, easily with TSD. From here you can reference your typings as per usual with a reference tag <reference path="<path>.d.ts" />. Check the alt-typescript-tutorial for more information and project examples.

Using Typescript 1.5 you can import with the legacy syntax:

import Alt = require("alt");
import chromeDebug = require("alt/utils/chromeDebug");
import AltContainer = require("alt/AltContainer");

Creating Actions

Actions are the way you update state. They're kind of a big deal.

alt.createActions :: Class -> Actions

class LocationActions {
  updateLocation(city) {
    return city;
  }
}

const locationActions = alt.createActions(LocationActions);

You return the data from your action that you wish to dispatch. If you want to run async in your actions then you simply return a function where the first argument is the dispatch:

class LocationActions {
  updateLocationThatDoesItAsync(city) {
    return (dispatch) => {
      setTimeout(() => dispatch(city));
    };
  }
}

alt.createActions then returns an Object containing all the methods defined. You can then call your actions directly.

locationActions.updateLocation('Paris');

Writing out actions that pass data through directly can get quite tedious so there's a shorthand for writing these what are essentially identity functions

class LocationActions {
  constructor() {
    // for single action
    this.generateActions('updateLocation');

    // as well as for many actions
    this.generateActions('updateCity', 'updateCountry');
  }
}

const locationActions = alt.createActions(LocationActions);
locationActions.updateLocation('Las Vegas')

locationActions.updateCity('Las Vegas')
locationActions.updateCountry('US')

Remember, dispatch only takes one argument. Therefore, if you need to pass multiple arguments into a store you can use an Object.

class LocationActions {
  updateLocation(x, y) {
    return { x, y };
  }
}

const locationActions = alt.createActions(LocationActions);

locationActions.updateLocation('Miami', 'Florida');

A shorthand function created in the constructor will pass through the multiple parameters as an Array

class LocationActions {
  constructor() {
    this.generateActions('updateLocation'); // ['South Lake Tahoe, 'California']
  }
}

const locationActions = alt.createActions(LocationActions);

locationActions.updateLocation('South Lake Tahoe', 'California');

There's even a shorthand for the shorthand if all you're doing is generating a list of actions

const locationActions = alt.generateActions('updateLocation', 'updateCity', 'updateCountry');

Stores

Stores are where you keep a part of your application's state.

You can either define your stores as a class/constructor-prototype or as an Object.

alt.createStore :: Class, string -> Store

class LocationStore {
  constructor() {
    this.bindAction(locationActions.updateLocation, this.onUpdateLocation);

    this.state = {
      city: 'Denver',
      country: 'US'
    };
  }

  onUpdateLocation(obj) {
    const { city, country } = obj
    this.setState({ city, country });
  }
}

const locationStore = alt.createStore(LocationStore);

You can also use a regular old JavaScript Object to create your stores. This is more about aesthetic preference.

const locationStore = alt.createStore({
  displayName: 'LocationStore',

  bindListeners: {
    onUpdateLocation: locationActions.updateLocation
  },

  state: {
    city: 'Denver',
    country: 'US'
  },

  onUpdateLocation(obj) {
    const { city, country } = obj
    this.setState({ city, country });
  }
});

If you're creating a store using a class/constructor then you also have the option of assigning your state values to your instance directly and then you're able to update them in place.

function LocationStore() {
  this.city = 'San Francisco';
  this.country = 'US';
}

LocationStore.prototype.onUpdateLocation = function (obj) {
  this.city = obj.city;
  this.country = obj.country;
};

Store instances returned by alt.createStore can be listened to for updates by calling listen.

listen is meant to be used by your View components in order to await changes made to each store. It returns a function you can use to un-listen to your store.

locationStore.listen((data) => {
  console.log(data)
});

Alternatively, you can use the unlisten method. It takes in the same function you used for listen and unregisters it.

Another important method is getState, which returns a copy of the current store's state.

locationStore.getState().city === 'Denver'

Important Note

All defined methods in your Store class will not be available on the store instance. They are accessible within the class but not on the returned Object via alt.createStore. This ensures that stores have no direct setters and the state remains mutable only through actions keeping the flow unidirectional. If you want to attach public/static functions to your store the recommended method is to call the exportPublicMethods method from the constructor:

class LocationStore {
  constructor() {
    this.exportPublicMethods({
      myPublicMethod: this.myPublicMethod
    });
  }

  myPublicMethod() {
    const internalInstanceState = this.getState();
    return internalInstanceState;
  }
}

const locationStore = alt.createStore(LocationStore);

locationStore.myPublicMethod();

An alternative is to declare the method as static, which will cause alt to expose the method on the store:

// does the same thing as above except in a more magical way
class LocationStore {
  static myPublicMethod() {
    const internalInstanceState = this.getState();
    return internalInstanceState;
  }
}

Canceling An Event

If you don't want the store to inform the view of an action you can call this.preventDefault() (or you can return false) from inside an action handler method.

class LocationStore {
  constructor() {
    this.bindAction(locationActions.updateCity, this.onUpdateCity);

    this.state = {
      city: 'Portland',
      country: 'US'
    };
  }

  onUpdateCity(city) {
    this.setState({ city });

    // ensure the view never finds out
    this.preventDefault();
  }
}

const locationStore = alt.createStore(LocationStore);

Constants

I thought you said there were no constants? Well, yeah, sort of. The thing is, they're automagically created for you. Feel free to use them to bind your actions or use the method itself, whatever reads better in your opinion.

class LocationStore {
  constructor() {
    this.bindAction(locationActions.UPDATE_CITY, this.onUpdateCity);

    this.state = {
      city: '',
      country: ''
    };
  }
}

const locationStore = alt.createStore(LocationStore);

Listening To Multiple Actions

class LocationActions {
  constructor() {
    this.generateActions('updateCity', 'updateCountry');
  }
}

const locationActions = alt.createActions(LocationActions);

Using the function bindListeners you're able to specify which action handlers belong to which actions this way you have ultimate control over what gets called and handled.

The function bindListeners is the inverse of bindAction. bindListeners takes an object of action handlers as keys and actions as a value.

class LocationStore {
  constructor() {
    this.bindListeners({
      handleCity: locationActions.updateCity,
      handleCountry: [locationActions.updateCountry, locationActions.updateLatLng]
    });
  }

  handleCity(data) {
    // will only be called by locationActions.updateCity()
  }

  handleCountry(data) {
    // will be called by locationActions.updateCountry() and locationActions.updateLatLng()
  }
}

Alternatively, you can bind all the actions inside locationActions using the shortcut bindActions

class LocationStore {
  constructor() {
    this.bindActions(locationActions);

    this.state = {
      city: 'Austin',
      country: 'US'
    };
  }

  onUpdateCity(city) {
    this.setState({ city });
  }

  onUpdateCountry(country) {
    this.setState({ country });
  }
}

const locationStore = alt.createStore(LocationStore);

Actions who have a onCamelCasedAction method or an actionName method available in the store will be bound. In this example locationActions.updateCity will be handled by onUpdateCity. There is no difference between calling the action handler updateCity or onUpdateCity it's just a matter of aesthetic preference.

Managing Store Data Dependencies

waitFor is mostly an alias to Flux's Dispatcher waitFor. Here's an excerpt from the flux docs on what waitFor is designed for:

As an application grows, dependencies across different stores are a near certainty. Store A will inevitably need Store B to update itself first, so that Store A can know how to update itself. We need the dispatcher to be able to invoke the callback for Store B, and finish that callback, before moving forward with Store A. To declaratively assert this dependency, a store needs to be able to say to the dispatcher, "I need to wait for Store B to finish processing this action." The dispatcher provides this functionality through its waitFor() method.

You can use waitFor like so:

const dependingStore = alt.createStore(class DependingStore {
  constructor() {
    this.bindActions(someActions);

    this.state = { answer: 42 };
  }

  onRandom(answer) {
    this.setState({ answer });
  }
})

const locationStore = alt.createStore(class LocationStore {
  constructor() {
    this.bindActions(someOtherActions)

    this.state = {
      meaningOfLife: null
    };
  }

  onThings() {
    this.waitFor(dependingStore.dispatchToken);

    this.setState({ meaningOfLife: dependingStore.getState().answer });
  }
})

You can also waitFor multiple stores by passing in an Array: this.waitFor([store1.dispatchToken, store2.dispatchToken])

Views

Your choice of view isn't important to alt. What's important is to know how the view consumes the store's data, and that is via event listeners.

In this example I'll be using React, but you're free to use your library of choice.

class LocationView extends React.Component {
  // these are methods that work with `connectToStores` which connects
  // one or many stores to your component passing the state in as props.
  // you're free to choose how the state from the store is passed into the
  // component as props.

  // this automatically does the listening/unlistening for you as well as
  // handles the state changes
  static getStores() {
    return [locationStore];
  }

  static getPropsFromStores() {
    return locationStore.getState();
  }

  render() {
    return (
      <div>
        <p>
          City {this.props.city}
        </p>
        <p>
          Country {this.props.country}
        </p>
      </div>
    )
  }
}

// just make sure to wrap your component with connectToStores()
export default connectToStores(LocationView);

Full Circle

Restart the loop by making your views kick off new actions.

Alt Features

Snapshots

takeSnapshot :: ?...string -> string

Snapshots are a core component of alt. The idea is that at any given point in time you can takeSnapshot and have your entire application's state serialized for persistence, transferring, logging, or debugging.

Taking a snapshot is as easy as calling alt.takeSnapshot(). It can also take an optional number of arguments as strings which correspond to the store names you would like to include in the snapshot. This allows you to take a snapshot of a subset of your app's data.

Bootstrapping

bootstrap :: string -> undefined

Bootstrapping can be done as many times as you wish, but it is common to use when initializing your application. The alt.bootstrap() function takes in a snapshot (JSON string) you've saved and reloads all the state with that snapshot, no events will be emitted to your components during this process, so again, it's best to do this on init before the view has even rendered. If you need to emit a change event, you can use this.emitChange inside of your bootstrap life cycle method.

Bootstrap is great if you're running an isomorphic app, or if you're persisting state to localstorage and then retrieving it on init later on. You can save a snapshot on the server side, send it down, and then bootstrap it back on the client.

If you're bootstrapping then it is recommended you pass in a unique Identifier, name of the class is good enough, to createStore so that it can be referenced later for bootstrapping.

alt.createStore(LocationStore, 'LocationStore')

Rollback

rollback :: undefined

If you've screwed up the state, or you just feel like rolling back you can call alt.rollback(). Rollback is pretty dumb in the sense that it's not automatic in case of errors, and it only rolls back to the last saved snapshot, meaning you have to save a snapshot first in order to roll back.

Flushing

flush :: string

Flush takes a snapshot of the current state and then resets all the stores back to their original initial state. This is useful if you're using alt stores as singletons and doing server side rendering because of concurrency. In this particular scenario you would load the data in via bootstrap and then use flush to take a snapshot, render the data, and reset your stores so they are ready for the next request.

Recycling

recycle :: ?...string -> undefined

If you wish to reset a particular, or all, store's state back to their original initial state you would call recycle. Recycle takes an optional number of arguments as strings which correspond to the store's names you would like reset. If no argument is provided then all stores are reset.

Lifecycle Methods

When bootstrapping, snapshotting, or recycling there are special methods you can assign to your store to ensure any bookeeping that needs to be done. You would place these in your store's constructor.

bootstrap is called after the store has been bootstrapped. Here you can add some logic to take your bootstrapped data and manipulate it.

class Store {
  constructor() {
    this.on('bootstrap', () => {
      // do something here
    })
  }
}

init is called when the store is initialized as well as whenever a store is recycled.

class Store {
  constructor() {
    this.on('init', () => {
      // do something here
    })
  }
}

rollback is called whenever all the stores are rolled back.

class Store {
  constructor() {
    this.on('rollback', () => {
      // do something here
    })
  }
}

error is called whenever an error occurs in your store during a dispatch. You can use this listener to catch errors and perform any cleanup tasks.

class Store {
  constructor() {
    this.on('error', (err, actionName, payloadData, currentState) => {
      if (actionName === MyActions.fire) {
        logError(err, payloadData);
      }
    });

    this.bindListeners({
      handleFire: MyActions.fire
    });
  }

  handleFire() {
    throw new Error('Something is broken');
  }
}

See all the lifecycle methods

Single Dispatcher

A single dispatcher instance is made available for listening to all events passing through. You can access this via the dispatcher property: alt.dispatcher and listening to all events is as easy as

alt.dispatcher.register(console.log.bind(console))

Each store has a reference to the dispatcher as well

alt.createStore(class MyStore {
  constructor() {
    this.dispatcher.register(console.log.bind(console))
  }
})

Flexibility

You can choose to use alt in many ways just like you'd use flux. This means your asynchronous data fetching can live in the actions, or they can live in the stores. Stores may also be traditional singletons as in flux, or you can create an instance and have multiple store copies. This leads us into server side rendering.

Server Side Rendering

Alt was built with isomorphism in mind. This means that you can run full flux server-side and pick back up on the client-side.

There are two options for using flux on the server:

  • Keep stores as singletons, keep data loading synchronous, bootstrap, and flush.
  • Create multiple instances of flux and inject the context into your app.

Stores as Singletons

With this approach your stores are singletons. Any actions that load data must be synchronous, meaning you can fetch your data outside of actions and stores, and once done you fire off a synchronous action which loads the store. Alternatively, you can gather all of your data, and once complete, you call bootstrap() which seeds all the stores with some initial data.

Once you've completed loading the stores with data you call flush() which takes a snapshot to send to the client and then resets all the stores' state back to their initial state. This allows the stores to be ready for the next server request.

Flux Instances

Creating separate instances of flux rather than relying on singletons can help when building isomorphic applications.

The problem with singletons is that you need to manage them by clearing out all their state and reloading them with new state on every request because requests happen concurrently. This isn't a problem if you already have your data and just need to load it into flux, or if you don't want to share your data fetching logic with the client -- in which case you can just load all your data at once on the server and render once that is all complete.

Singletons only become a problem if you wish to share data fetching with client and server, don't want to use something like Render to define your data fetching at the component level, or if you have a really complex data fetching scheme where some fetches depend on the result of other ones. In these cases creating separate instances (or copies) keeps flux sandboxed to each request so other async requests won't mutate the state in the stores.

Taking this approach means you're making the trade-off of injecting the flux instance into your application in order to retrieve the stores and use the actions. This approach is similar to how fluxible solves isomorphic applications.

Creating a new alt instances is fairly simple.

class Flux extends Alt {
  constructor() {
    super();

    this.addActions('myActions', ActionCreators);
    this.addStore('storeName', Store);
  }
}

const flux = new Flux();
// client.js

React.render(
  <App flux={flux} />,
  document.body
);
// server.js
React.renderToString(<App flux={flux} />);
// retrieving stores
flux.getStore('storeName').getState();

// actions
flux.getActions('myActions');

Picking back up on the client

To help facilitate with isomorphism alt recommends you use iso, a helper function which serializes the data on the server into markup and then parses that data back into usable JavaScript on the client. Iso is a great complement to alt for a full-stack flux approach.

Converting a flux application to alt

  1. Importing the chat project.
  2. Adding alt and removing boilerplate.
  3. Converting some actions and the last action.
  4. Converting the stores MessageStore, ThreadStore, and UnreadThreadStore.
  5. Finishing touches.

Differences Example

Flux has constants, the dispatcher is also pretty dumb as in it just takes what you passed in the action and pipes it through to the store. This is completely fine but not something you should be expected to write. The nice thing about constants is that you can easily grep for them in your application and see where all the actions are being called, with alt you get the same benefit without having to manage them.

Before: Flux

var keyMirror = require('keymirror');

var actionConstants = keyMirror({
  HANDLE_ACTION: null
});

var action = {
  foo() {
    AppDispatcher.handleAction({ type: actionConstants.HANDLE_ACTION, data: 'foo' })
  }
};

var AppDispatcher = Object.assign(new Dispatcher(), {
  handleAction(payload) {
    this.dispatch(payload);
  }
});

After: Alt

class Action {
  handleAction() {
    return 'foo';
  }
}

const action = alt.createActions(Action);

TL;DR

  • Isomorphic
  • Pure Flux
  • No constants
  • No static string checking
  • No giant switch statement
  • Save state snapshots
  • Rollbacks
  • Bootstrap components on app load
  • Light-weight and terse
  • ES6 Syntax, code your actions and stores with classes
  • Flexible
  • No direct setters on stores
  • Single dispatcher
  • Global listening for debugging
  • Small library

License

MIT

alt's People

Contributors

af7 avatar afilp avatar bear-foot avatar benstokoe avatar caasi avatar chetstone avatar chrishoage avatar danielericlee avatar david0178418 avatar epic-tactix avatar ericsong avatar ericwooley avatar frederickfogerty avatar goatslacker avatar indus avatar jareware avatar jbhatab avatar jdlehman avatar mareksuscak avatar mattkahl avatar mattmccray avatar mikekidder avatar mull avatar rocketraman avatar sloria avatar srph avatar talyssonoc avatar tony99nyr avatar troutowicz avatar wbreakell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alt's Issues

Asynchronous in Store

How do I implement asynchronous? like this:

var alt = new Alt;

var Actions = alt.createActions(class {

  constructor() {
    this.generateActions('doSomething');
  }

});

var Store = alt.createStore(class Store {

  constructor() {
    this.bindAction(Actions.doSomething, this.doSomething);
  }

  doSomething() {
    this.done = false;

    setTimeout(() => {
      this.done = true;
    }, 1000);
  }

});

Store.listen((data) => {
  console.log(data);
});

Actions.doSomething();

The example output to the console only this:

{ done: false }

Util for working with immutableJS

There should be a util that hooks into Alt's lifecycle methods to automatically serialize and deserialize any immutablejs props you have defined in the store.

Add a changelog

Although we're not in strict semver territory just yet I still want to keep a changelog. This issue is so I can add a changelog to all the previous releases.

Time Traveling

Original:

With DispatcherRecorder you can replay certain events in your application. It would be cool if there was a util that would allow you to timetravel through your application but also allow you to specify if you'd only like to time travel certain stores.

Update:

I've got a util that lets you TimeTravel through your entire application's state. It uses alt (very meta) for the time travel bit. You have a set of actions: revertTo, revertStoreTo, and destroyAll which will revert to a specific point in time, revert a specific store to a point in time, or destroy all the stored events.

My only concerns at this point are with memory usage. This should probably use something like immutable or some cursors so we can keep memory usage pretty low.

I need help now fleshing out the rest of the API and most importantly if you've got feature requests then send them in :)

Open questions:

  • If people want to be able to select certain values to be cursors then what does that look like? Which library should we use if any?
  • What other features should this support?
  • Thoughts on what the API should look like?

How to test action callbacks in stores?

If I have a store like this:

module.exports = alt.createStore(class SomeStore {

  constructor() {
    this.bindActions(SomeActions);
  }

  onSomeAction(parameter) {
    // Code to test...
  }

});

How would I test onSomeAction()?

I can't find a way to call this method from the test as it is not accessible from the outside. I also tried calling SomeActions.someAction() from the test, but onSomeAction() didn't end up being called.

What is the recommended way of doing this?

emitChange within Store

Is there a way to emit a change from within a store? I'll give you an example where this would be helpful:

class TaskStore
  constructor: ->
    @bindActions(App.Actions)
    @collection = []

  onSearch: (query) ->
    $.ajax
      url: '/search/tasks'
      method: 'get'
      dataType: 'json'
      data: {q: query}
      success: (results) =>
        @collection = @collection + results
        # currently not possible
        # @emitChange()
        # what I have to do instead is access the store from "outside"
        App.Stores.TaskStore.emitChange()
    false # prevent the default change emit, because nothing has changed until the success callback fires

App.Stores.TaskStore = alt.createStore(TaskStore)

So as you can see, my action runs and triggers an ajax call. The state of the store will not have changed until the ajax call completes. It would be great to be able to emit a change from within that success callback. What do you think?

Webpack build warning

Hey,

I just started using webpack, and it spits out this warning when building:

WARNING in .//alt/dist/alt.js
Critical dependencies:
3:434-441 This seem to be a pre-built javascript file. Even while this is possible, it's not recommended. Try to require to orginal source to get better results.
@ ./
/alt/dist/alt.js 3:434-441

Not sure if it is a webpack issue, or an alt issue. I havn't noticed any problems so far, but since it's the only warninig it would be nice to get it fixed ๐Ÿ˜„

Alt getting started sample app

What should the example app be?

I wouldn't like to do a TodoList. Already beaten to death.
The chat is already available as an example.
Email client, perhaps. But there are examples already out there.
Github search? Perhaps, but it's a bit narrow.
HN client. Also probably narrow.
RSS reader?
Music player?
A game?

Ideally this example app would:

  • Be simple. Around 2 stores, 1 action
  • Have asynchronous fetching involved.
  • Have a store dependency so we can use waitFor
  • Not have a very complex UI interaction, because the focus isn't the View layer.

Own state in combination with ReactStateMagicMixin

Is it possible to add own properties to the state, if it is managed by the ReactStateMagicMixin? Or should be everything put into stores?

Maybe the need for additional properties also means that one should refactor the component? I'm not sure about this. Would be interested in hearing some opinions.

Alt devtool

Developer tool (Chrome first) for using Alt.

You can help by discussing things you want to see in it and by contributing that code.

alt.bootstrap: `TypeError: Cannot read property 'Symbol(...`

Hi,

I have issue using alt + iso, it keeps throwing me:

TypeError: Cannot read property 'Symbol(1424950030126the state container)' of undefined
   at /Users/iam4x/github/es6-react-boilerplate/node_modules/alt/dist/alt.js:224:32
   at Array.forEach (native)
   at setAppState (/Users/iam4x/github/es6-react-boilerplate/node_modules/alt/dist/alt.js:223:20)
   at Alt.bootstrap (/Users/iam4x/github/es6-react-boilerplate/node_modules/alt/dist/alt.js:460:9)
   at module.exports (/Users/iam4x/github/es6-react-boilerplate/server/server.router.jsx:14:7)
   at Layer.handle [as handle_request] (/Users/iam4x/github/es6-react-boilerplate/node_modules/express/lib/router/layer.js:82:5)
   at trim_prefix (/Users/iam4x/github/es6-react-boilerplate/node_modules/express/lib/router/index.js:302:13)
   at /Users/iam4x/github/es6-react-boilerplate/node_modules/express/lib/router/index.js:270:7
   at Function.proto.process_params (/Users/iam4x/github/es6-react-boilerplate/node_modules/express/lib/router/index.js:321:12)
   at next (/Users/iam4x/github/es6-react-boilerplate/node_modules/express/lib/router/index.js:261:10)

And the example https://github.com/goatslacker/iso/tree/master/examples/react-router-flux is doing the same.

How can I help?

Add hook/event after action was dispatched.

I think snapshotting functionality isn't that usable on the client, because snapshots usually should be done at predefined moments in app lifecycle. In Flux this is usually after some action(s) completed. But I wasn't able to find proper hook in the Alt. It could be better if we could use it like:

alt.actionDispatched = (action) => {
...
}

Or something like this.
actionDispatched should be called after Dispatcher.dispatch(...) method completes.

Add "magic" of alt section to readme

Alt by design implicitly makes class methods immutable/private to everything but actions. This keeps the flow unidirectional. However, alt provides an exception to this functionality by using static methods. When a method is declared static, alt magically adds the method to the object returned from alt.createStore(), giving public access to the method. This behavior is strange, confusing, and twists the expected behavior of the static method type. static methods are meant to be properties of an object, not the properties of an object's instance.

A better solution is to use a mixin that provides the ability to explicity state which methods of a class should be made public in the resultant altStore instance. See PR #66.

I propose this static method support be dropped from alt and replaced by something akin to what is described above.

Store class inheritance

The Store class inheritance doesn't work:

class BaseStore {
  static doSomethingOnBase() {
    console.log('doSomethingOnBase()');
    this.emitChange();
  }
};

class Store extends BaseStore {
  static doSomething() {
    console.log('doSomething()');
    this.emitChange();
  }
}

var storeInstance = alt.createStore(Store);

storeInstance.listen(() => {
  console.log('Triggered.');
});

console.log(BaseStore.doSomethingOnBase);

storeInstance.doSomething();
storeInstance.doSomethingOnBase();

BaseStore.doSomethingOnBase still exists but storeInstance.doSomethingOnBase is undefined.

Alt without singletons but with flux instances

Hi, I'm just trying alt's flux instances feature - https://github.com/goatslacker/alt#flux-instances. Unfortunately there is a circular problem:

In order to create actions and stores I need an instance of Alt class

ArticleActions.js

import flux from '../../flux'; // instance of alt is created here

class ArticleActions {
    constructor() {
        this.generateActions('requestArticles');
    }
}

export default flux.createActions(ArticleActions);

But in order to create alt instance and register stores and actions I need them already created:

flux.js

import Alt from 'alt';

// all of them require this file
import RouterActions from './shared/actions/RouterActions'; 
import ArticleActions from './pages/actions/articles/ArticleActions';
import ArticleStore from './pages/actions/articles/ArticleStore';

class Flux extends Alt {
    constructor() {
        super();

        // register flux actions and stores
        this.addActions('router', RouterActions);
        this.addActions('articles', ArticleActions);
        this.addStore('articles', ArticleStore);
    }
}

export default new Flux();

Future of Alt

This is just to openly discuss any ideas on where you'd like to take alt or what you'd like to do with it. Perhaps actionable tasks can come out of this discussion.

Server-side render with Flux instances

One more question for you.

I'm trying to server-render my Alt-based Flux application, that's very rough example of what I'm doing on server request:

var React = require('react');
var Router = require('react-router');
var routes = require('./src/routes.jsx');
var webAPIUtils = require('./src/webAPIUtils');

app.use('/', function (req, res, next) {
        var flux = require('./src/flux');
        webAPIUtils.getFeeds(function (err, feeds) {
            if (err) {
                return next(err);
            }

            flux.getActions('articles').receiveFeeds(feeds); // sync action
            Router.run(routes, req.url, function (Handler) {
                var content = React.renderToString(React.createElement(Handler, {flux: flux}));
                res.render('base', {
                    title: title,
                    content: content,
                    mainJs: mainJs,
                    mainCss: mainCss
                });
            });
        });
});

That's client:

import 'babelify/polyfill';

import 'es5-shim';
import 'es5-shim/es5-sham';

import React from 'react';

import router from './router';
import flux from './flux';

window.React = React;

React.initializeTouchEvents(true);

router.run((Handler, state) => {
    flux.getActions('router').routeChange(state);
    React.render(
        React.createElement(Handler, { flux }),
        document.getElementById('android-hybrid-app')
    );
});

and how flux.js looks like:

import Alt from 'alt';

import RouterActions from './shared/actions/RouterActions';
import ArticleActions from './pages/articles/ArticleActions';
import ArticleStore from './pages/articles/ArticleStore';

class Flux extends Alt {
    constructor() {
        super();

        // register flux actions and stores
        this.addActions('router', RouterActions);
        this.addActions('articles', ArticleActions);
        this.addStore('articles', ArticleStore);
    }
}

export default new Flux();

But unfortunately ArticleStore on client doesn't contain the result of action which was fired on server.

Better rollback system

Currently rollback only works if you've recently taken a snapshot. Perhaps there can be a way to enable automatic rollbacks in case something goes wrong. Ideally this would add minimal code.

Issue with Store.listen/Store.unlisten in ES6 and .bind(this)

I'm using React 0.13-rc and writing the components in ES6

The problem is that when the callback method is bound to thisin both the listen and unlisten calls the event isn't removed in the "removeListener". So next time the component loads the callback is added again, then again, and...

class MyComponent extends React.Component {
    componentDidMount () {
        MyStore.listen(this.onStoreChange.bind(this));
    }
    componentWillUnmount () {
        MyStore.unlisten(this.onStoreChange.bind(this));
    }
    onStoreChange () {
        // Event "change" is not removed so on next "onStoreChange"
        // it will be called 2, then 3, 4.... Because it was added again in the
        // componentDidMount method
    }
}

class MyComponent extends React.Component {
    componentDidMount () {
        MyStore.listen(this.onStoreChange);
    }
    componentWillUnmount () {
        MyStore.unlisten(this.onStoreChange);
    }
    onStoreChange () {
        // This works but "this" is the wrong context so no `this.setState()`
    }
}

Any ideas? I'm not really sure why this would be a problem. I'll look into it but hopefully this is something easy and I'm missing something ;)

P.S. Great work! Im loving Alt (used reflux) and tried all the others

how to call action from same action class?

I'm able to workaround the scope issue by creating a var outside the class, but it makes me feel dirty. Is there a cleaner way? I know the typical pattern is to call out to another module, which can then create the action as normal. Is there another way?

var self
class TestActions() {
  action1() {
    //do stuff, maybe talk to an API
    self.action2()
  }
}
self = alt.createActions(TestActions)
module.exports = self

Actions with same names are responded by different stores

This looks like a major issue.

I have 2 actions creator classes with same actions generated. These actions receive different data:

// MatchActions.js
export default class MatchActions {
    constructor() {
        this.generateActions('receiveLeagues');
    }
}

// MyClubActions.js
export default class MyClubActions {
    constructor() {
        this.generateActions('receiveLeagues');
    }
}

Both of them are added via Flux instances:

class Flux extends Alt {
    constructor() {
        super();

        this.addActions('matches', MatchActions);
        this.addStore('matches', MatchStore);

        this.addActions('myclub', MyClubActions);
        this.addStore('myclub', MyClubStore);      
    }
}

The problem is that listen methods of both stores are fired:

// MyClubStore.js
constructor() {
    let myClubActions = this.alt.getActions('myclub');
    this.bindActions(myClubActions);
}

onReceiveLeagues(leagues) {
     // is fired on Match actions as well..
     console.log(leagues);
}

// MatchStore.js
constructor() {
    let matchActions = this.alt.getActions('matches');
    this.bindActions(matchActions);
}

onReceiveLeagues(leagues) {
     // is fired on MyClub actions as well..
     console.log(leagues);
}

ES5 Examples

Hey there,

I am interested in using alt for my current project and at the same time have decided to not use ES6, are there any ES5 examples of using Alt?

Thanks.

Snapshots of only certain stores

Right now takeSnapshot takes the entire application snapshot. It would be nice if you could take a snapshot of a subset of your application.

Create a util for data fetching

This would settle the "how to do async" debate.

I wouldn't want this in core but I'm sure some of core would have to change so this can be supported first-class.

This util can be flexible so async can exists in actions or in stores or it can have a strict opinion.

Perhaps several interfaces can be included so this can all be hooked up easily with popular cloud butt based systems

Use of JS objects as sets/maps instead of ES6 Maps

Hello!

So, I noticed that in the Alt class and in the Store class, you use the normal JS pattern of objects-as-maps. Example would be here:

// from Alt class constructor
this.actions = {}
this.stores = {}

// from Store contstructor
this[LIFECYCLE] = {}
this[LISTENERS] = {}

The use of this as an overloaded object-as-map seems central to a lot of the code, but for the examples I gave it's not as clear.

Why not use the ES6 Maps for this purpose?

Calling store static methods from within other store statics

You are currently unable to call a store static method from within another static method using this. This can be limiting in some cases, and the workarounds are ugly. I realize there might be some design challenges, but I'd love to be able to do, e.g.:

static foo(a1) { return this.getState().foo[a1]; }
static bar() { return this.foo('baz'); }

Calling actions with isomorphic flux instances

Hey, it would be nice to hear your point of view on the following.

I am using Alt inside isomorphic app which uses alt's flux instances feature. Currently my actions that deal with API look like:

import { getStats } from '../../../webAPIUtils';

export default class MatchDetailActions {
    constructor() {
        this.generateActions('receiveStats', 'failReceiveStats');
    }

    requestStats(requestOptions) {
        this.dispatch(requestOptions);

        // method just calls superagent with necessary url
        getStats(requestOptions, function (err, stats) {
            if (err) {
               this.actions.failReceiveStats(err);
               return;
            }
            this.actions.receiveStats(stats); // calling actions inside action (bad?)
        }.bind(this));
    }
}

As you see I'm calling success/error actions inside the initial request action which is considered an anti-pattern. But this allows me to call such actions on the server side without worrying how to pass the flux instance created by request inside web api utils.

What is your approach in similar situation?

waitFor() semantics

Currently the waitFor method is pretty much the same as the stock Facebook one:

this.waitFor([ AStore.dispatchToken, OtherStore.dispatchToken ])

I'd like to make the dispatcher entirely an implementation detail. The waitFor method can extract the dispatcherTokens itself.

To me, this reads much nicer:

this.waitFor( AStore, OtherStore)

Or, if you feel strongly about passing an array instead of iterating the arguments:

this.waitFor([ AStore, OtherStore ])

What do you think? I'd be happy to create a PR if it's something you think fits with alt's vision of Flux.

Add a contributing

Contributing guide featuring:

How to get setup.
Pull request style.
Code style.

Chat example not running

WARN: Dropping unused function argument exports [-:1,437]
WARN: Dropping unused function argument module [-:1,430]

I get like a 100 of these warnings when I run the command "npm run build" (obviously I run this only after installing the dependencies) for the chat example. Have any idea of what's going wrong?

Cannot push on undefined in Suscribe.js with 0.13.10

Similar issue that you've been working on. Things were working for me with 0.13.9, but with .10 that array creation issue seems to have shifted over to the Subscribe.js mixin. I get: Cannot read property 'push' of undefined.

Looking through the code, I see where you call add on the subscribe mixin from within the listener mixin, but I don't see where the create method was called.

error thrown on listenTo(store, onChange)

On the newest version, 0.13.7, I am getting an error when using the listenTo() function (when using the ListenerMixin).

The error: Uncaught TypeError: Cannot read property 'bind' of undefined

It is being thrown on fn.bind(context) here in the add function:

var MIXIN_REGISTRY = '_alt store listener registry_'

    var Subscribe = {
      create: function (context) {
        context[MIXIN_REGISTRY] = []
      },

      add: function (context, store, fn) {
        var handler = fn.bind(context)
        context[MIXIN_REGISTRY].push({ store: store, handler: handler })
        store.listen(handler)
      },

      destroy: function (context) {
        context[MIXIN_REGISTRY].forEach(function (x) {
          x.store.unlisten(x.handler)
        })
        context[MIXIN_REGISTRY] = []
      }
    }

    module.exports = Subscribe

I was making a new project when it happened and reverted back to 0.13.2 that I was using on previous projects and it works fine now.

How to send another action as a consequence of an action?

Is there any way to send another action as part of the handling of another action inside a store? And maybe this is more of a 'this isn't the flux way' question, but I'm having trouble trying to google for anybody trying to do this sort of thing.

So here's my setup. I'm essentially making a bejeweled clone, and I'm trying to get the cascading clears to work using flux actions. I have a swapTile action, but after every time the board changes as a result of this action, I need to check if any other clears should be happening. I tried sending another action from inside the first swapTile action, but it just hangs. From what I understand (granted this is limited), this is to be expected from the way the dispatcher works.

I had the thought of essentially having the store save some data which would essentially tell the view to trigger the second cascade event, but I'm not sure I really like the fact that the Store (where most of this logic should live) relies on the view to send the next event(s). Is this the only way to do this? Or is there a more flux-y way to do this sort of thing?

Mixins throw warning for unnecessary calls to bind in React 0.12.2

Have tried including the ListenerMixin, FluxyMixin, and ReactStateMagicMixin, all of which throw the following messages in React v0.12.2:

bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See Header

ReactCompositeComponent.js?cd59:1324 bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See IndexRoute

ReactCompositeComponent.js?cd59:1324 bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call. See App

These are caused by line 13 of alt/mixins/Subscribe.js.

  add: function (context, store, fn) {
    if (!context[MIXIN_REGISTRY]) {
      Subscribe.create(context)
    }
    //
    var handler = fn.bind(context) // React v0.12.2 does not like this line.
    //
    context[MIXIN_REGISTRY].push({ store: store, handler: handler })
    store.listen(handler)
  },

Important to note that nothing actually breaks, but the messages will turn into a nasty error message saying that there are issues and the unminified code should be inspected when run in production.

Uncaught TypeError: Illegal invocation when registering dispatch in a store

So I'm trying to follow your example of:

alt.createStore(class MyStore {
  constructor() {
    this.dispatcher.register(console.log)
  }
})

My alt looks like this:

import Alt from 'alt';

var altFlux = new Alt();

export default altFlux;

My store looks like this:

import altFlux from '../altFlux';
import AlertMessageActions from '../actions/AlertMessageActions';
import UserDataActions from '../actions/UserDataActions';

class AlertMessageStore {
  constructor() {
    this.dispatcher.register(console.log);

    this.bindActions(AlertMessageActions);
    this.bindAction(UserDataActions.receiveUserDataError, this.onReceiveAlertError);

    this.message = null;
    this.alertLevel = null;
    this.error = null;
    this.alertVisible = false;
    this.dismissAfter = null;
  }

  onReceiveAlertSuccess(msg) {
    this._initGenericAlert('success', msg);
  }

  onReceiveAlertError(msg) {
    console.log('GOT AN ERR!');
    this._initErrorAlert(msg);
  }

  _initGenericAlert(lvl, msg) {
    this.alertLevel = lvl;
    this.message = msg;
    this.error = false;
    this.alertVisible = true;
    this.dismissAfter = 15000;
  }

  _initErrorAlert(msg) {
    this.alertLevel = 'danger';
    this.message = msg;
    this.error = true;
    this.alertVisible = true;
    this.dismissAfter = null;
  }
}

export default altFlux.createStore(AlertMessageStore);

My action:

import altFlux from '../altFlux';
import UserDataAPI from '../utils/api/UserDataAPI';
import ErrorToJSON from '../utils/serializers/ErrorToJSON';

class UserDataActions {
  constructor() {
    this.generateActions(
      'receiveUserData',
      'receiveUserDataError'
    );
  }

  getUserData(params = { useCache: true }) {
    this.dispatch();

    let localData = UserDataAPI.getLocalUserData();

    if (localData && params.useCache) {
      this.actions.receiveUserData(localData);
    } else {
      UserDataAPI.getUserData().then((res) => {
        this.actions.receiveUserData(res.body.data);
      }).catch((err) => {
        let error = ErrorToJSON.superagent(err);

        this.actions.receiveUserDataError(error);
      });
    }
  }
}

export default altFlux.createActions(UserDataActions);

My view:

import React from 'react';
import { Jumbotron, Button, ButtonToolbar, ModalTrigger } from 'react-bootstrap';
import NewUserHelpModal from './NewUserHelpModal';
import UserDataActions from '../../actions/UserDataActions';

var NewUserMessage = React.createClass({
  componentWillReceiveProps(nextProps) {
  },

  componentDidUpdate(prevProps, prevState) {
    console.log('not ready!');
  },

  _fetchUserData() {
    UserDataActions.getUserData({ useCache: false });
  },

  render() {
    return (
      <Button bsStyle='success' onClick={this._fetchUserData}>Fetch User Data</Button>
   )
}

However, when I click the button to trigger the action, in my console log I get:

Uncaught TypeError: Illegal invocation
  Dispatcher.prototype.$Dispatcher_invokeCallback=function(id) {
    this.$Dispatcher_isPending[id] = true;
    this.$Dispatcher_callbacks[id](this.$Dispatcher_pendingPayload);
    this.$Dispatcher_isHandled[id] = true;
  };
this.$Dispatcher_callbacks[id](this.$Dispatcher_pendingPayload);

My intention is when the user clicks the button, it calls the UserDataActions.getUserData and then if it fails, the error is sent to the AlertMessageStore.

Removing the line this.dispatcher.register(console.log), and everything works OK. Any ideas? Am I missing something?

Add documentation for mixins

I did not notice that there were several other mixins available (other than the ListenerMixin) until I was going through the source code. Some have helpful inline docs, but a Mixin section in the README would be very helpful in acquainting users with what is available and a quick example of usage.

circular dependency between action and webapi

I was looking at the chat example and the pattern of an action calling the webapi which then calls another action after receiving the response from the API. If the web API wants to call an action from the same class as the original action, it results in a circular dependency with the require modules:

ActionClass -> requires WebAPI -> requires ActionClass

Any thoughts on how to structure this better?

(Un)serializing dispatched actions

I was intrigued by the DispatcherRecorder, so I went about trying build a debugging tool that would take a snapshot and then record some actions. I would like to be able to serialize the snapshot and events so they could be shared and replayed later or on another machine.

However, in order for this to work, I have to modify this line to use Symbol.for() instead of Symbol() so that I can replay the events after unserializing.

I see why you're not using Symbol.for to avoid conflicts in action classes/names, but I think it could be useful to provide a way to use Symbol.for.

I'm imagining something like:

const alt = new Alt({ exposeActions : true });

or for each action class

class MyActions { ... }
alt.createActions(MyActions, exports, { expose : true });

Perhaps there's another way to achieve this without modifying alt. Here's my work-in-progress (apologies for the spaghetti code -- it's getting a bit late for me).

Cannot dispatch in the middle of a dispatch

From a Login react component, I have a login action that makes an API request and on successful response, it updates a SessionStore. The Login component sees the value of the session store updated through it's props passed down from the parent component, and in shouldComponentUpdate, it does a transitionTo (using react-router) to an account page. The account page then calls an action to update the SessionStore in ComponentWillMount.

I'm getting this error message:
Uncaught Error: Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.

I believe it's due to the account page trying to update the SessionStore as the result of the SessionStore just getting updated from a previous action. Any suggestions on how to architect this differently?

NOTE: things work when using react-router's HashLocation, but not with the HistoryLocation.

Use with `react-hot-loader`

Using alt with react-hot-loader loader in webpack, throw me random errors:

Cannot not apply hot update to users.jsx: A store named UserStore already exists, double check your store names or pass in your own custom identifier for each store

I couldn't find what isn't working, went through this issue: moreartyjs/todomvc-moreartyjs#2

Maybe something with the context?

How do you initialize a store with data?

I've looked through your examples, but I haven't found a way to populate a store with existing data. I see you set values in the constructor, but the values are always hard coded.

I'm interested in something like this:

class ProjectStore {
  constructor(value) {
    self.value = value
  }
}

alt.createStore(ProjectStore, {foo: "Bar"})

Is this something that's planned, or is there a preferred way of achieving this same effect?

By the way, awesome job with Alt, I'm really liking it over some of the other Flux implementations I've tried!

Store Mixins

What do you think about adding store mixins? Mixins would be able to add bindings to actions, and define the functions they get bound to. There would also need to be a way to pass information into the mixins from the store. Although, this could also be achieved through a sort of Mixin Generator.

I've got a certain bit of functionality that I'm having to clone to pretty much every Store I have. It pertains to listing for an action that fires when the app gets new data from a websocket. It needs to grab the data out of the object at the key that matches the store's name TaskStore would grab "tasks", etc. So the mixin would need to be able to get that unique "data_key" that it needs to look for in it's action handler.

Does this sound like a good idea? Or would it be better to just leave it to the user to implement mixins if they need it?

Store immutability

Alt looks like it's got a lot of good ideas on improving Flux.

The README talks about store immutability, but I don't see much pertaining to that in the code, nor do I see dependencies on e.g. Immutable.js or mori.

Could you elaborate a bit on this?

Add more examples

Would be nice to see a more complex/realistic example using alt. Showing:

  • full CRUD
  • with asynchronous remote data fetching/storing
  • optimistic rendering with error handling
  • react-router integration
  • user authentication

Basically a full app.

Is there perhaps such an example for one of the other flux libs that would easily translate to alt?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.