GithubHelp home page GithubHelp logo

reflux / refluxjs Goto Github PK

View Code? Open in Web Editor NEW
5.4K 5.4K 332.0 739 KB

A simple library for uni-directional dataflow application architecture with React extensions inspired by Flux

License: BSD 3-Clause "New" or "Revised" License

JavaScript 100.00%

refluxjs's People

Contributors

bripkens avatar bryangrezeszak avatar ccapndave avatar dan-weaver avatar dashed avatar devinivy avatar deviousdodo avatar dtinth avatar ericclemmons avatar heldr avatar iofjuupasli avatar ivan-kleshnin avatar krawaller avatar kyleamathews avatar maxguzenski avatar mikhuang avatar patrick-webs avatar richardlitt avatar robcolburn avatar rymohr avatar shraymonks avatar simondegraeve avatar snorp avatar spoike avatar srph avatar turbo87 avatar undozen avatar vcarl avatar vslinko avatar willembult 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  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

refluxjs's Issues

Circular checks are broken

Listening to stores in components are broken in 0.1.8.

More info will come as I am debugging this now.

Throttled actions

Add the possibility to create actions that are throttled, i.e. they only fire at regular intervals, for actions where it is known that it will get spammed from user events (most likely from mouse events) but where listeners don't need to listen to every invocation and is only interested in a reduced set of invocations.

The suggestion is that users that invoke the action can choose to use a throttled version instead:

// Create actions as usual:
var myAction = Reflux.createAction();
myAction.listen(function(num) {
  console.log(num);
});

// Get a throttled variation of `myAction`:
var throttledAction = myAction.throttled(50);

// Test:
var i = 0;
for(;i <= 10; i++) {
  throttledAction(i);
}
// Should output: 
// 0
// 10

Add .jshintrc

For better code standards and less merge conflicts!

As mentioned in #13

Handle events efficiently

Getting this warning message from EventEmitter when creating a lot of stores and actions.

(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit.

Need to rewrite event handling a bit, since for larger web applications there will be a lot of event emitters around so this warning is not very interesting to have.

Extend the listener mixin to provide "initial" data

Add the possibility to fetch initial data from a data store to a React component. This needs a change to the ListenerMixin's listenTo method and in the store implementation as well.

Motivating Example

var aStore = Reflux.createStore({
    init: function() {
        this.listenTo(anAction, function() {
            this.trigger('not initial data anymore');
        });
    }
    getInitialData: function() {
        return "the initial data";
         // is used by Listenermixin if a component wants initial data 
    }
});

var Component = React.createClass({
    mixins: [Reflux.ListenerMixin],
    getInitialState: function() {
        return {}; // <-- impossible to know the state if stores aren't initialized yet
    },
    componentDidMount: function() {
        this.listenTo(aStore, changeCallback, changeCallback);
            // will call changeCallback twice, during store change and 
            // when the store has initialized
            // third callback should be optional
    },
    changeCallback: function(data) {
        console.log(data);
        this.setState({
            data: data
        });
    }
});

In the example, before invoking the anAction the component should be able to set the state with "the initial data" fetched from the store's getInitialData method. When invoking the anAction the component should be able to set the state with "not initial data anymore" fetched from the change event.

Thoughts?

Make proper mixin interfaces for Store and Action interfaces

Refactor createAction and createStore factory methods to use object literals defining an Action and an Store respectively to be used as mixins when creating the action functors and store objects. We're sort of doing this in createStore but we're creating new Store functions and extending them, I feel we can move the Store prototype outside to an object literal and have that mixed into the final store object's prototype.

This comes with two benefits:

  • Makes it possible for others to extend the Action and Store mixins themselves (probably expose this through Reflux.mixins.Action and Reflux.mixins.Store properties or something)
  • Making documentation more straightforward once we need to do API docs for the project homepage. This refs #2.

Reflux.all() also needs Reflux.waitFor()?

First, thanks Reflux, I'm enjoying using it!

I have an aggregate store that depends on two others:

Store1 Store2
\ /
AggregateStore

Right now I have AggregateStore listening to the other stores using listenTo. This works, but the aggregate computation can be quite expensive. So if I have some action that touches both Store1 and Store2 the result is that the aggregate computation runs twice. This gets expensive, and makes debugging a bit trickier since the flow passes through the aggregate function multiple times for a single action.

I looked at using Reflux.all to solve the problem. It solves the multiple aggregate computations nicely but introduces a new problem.

Some actions only affect Store1, and in that case I'd still like my AggregateStore to still update immediately with respect to the original action: using the new value from Store1 and the existing value from Store2. But Reflux.all will only fire after both Store1 and Store2 have fired. So in some cases my AggregateStore isn't updated for a long time.

I think Reflux.all() is nice, but I think it also needs a Reflux.waitFor() counterpart that works more like Facebook Flux's waitFor(). Is something like this in the works, or are there suggested workarounds?

Add hooks for actions

As mentioned in #14, add hooks for actions: preEmit and shouldEmit. Reminiscent of life cycle functions in React.

Error is thrown depending on order when listeners are registered

The following error is produced in certain configurations on this line:

Uncaught TypeError: Cannot read property 'hasListener' of undefined

This happens when you register composed listeners before other stores and actions. The args array has a length set, but is an array of undefined.

Testcase pseudo code:

// Inside a store init
var join = Reflux.all(testAction1, testAction2);

this.listenTo(join, resetCallback);
this.listenTo(testAction2, commonlyCalledCallback); // throws the error from here

Workaround is to listen to the joined actions/stores afterwards:

this.listenTo(testAction2, commonlyCalledCallback);
this.listenTo(join, resetCallback);
// no errors thrown, works as expected

Remove lodash dependency

First of: I really like where you are going with reflux and you mentioned many of my pain points with Flux, thanks!

It seems like the lodash dependency could be removed by implementing a custom assign and isFunction functions. Removing it seems desirable to reduce reflux's impact on page load time.

Using reflux with browserify

I don't know if this is something wrong with my project configurations or with reflux, but when I try to use browserify, I get the following error:

Error: Cannot find module 'browserify-shim' from 'PROJECT_PATH/node_modules/reflux'

Any suggestions?

pubsub vocabulary

Salvaging the discussion from the closed PR; currently we talk about listeners and publishers. This is a slightly unfortunate mix of vocabs, listener/listenable and subscriber/publisher.

Should we not care, or maybe move to publisher subscriber all across? As @spoike said, listener/listenable is hard to distinguish and doesn't really roll off of the tongue.

Textarea's cursor jumps when changing its value through reflux

Hello,

I'm experiencing an issue where if I update a textarea's value through the Action โ†’ Store โ†’ View flow, with the cursor not at the end, the cursor will jump to the end. The same doesn't happen if I use state internally in the component to keep track of the current value, like in this example.

Does this by an off chance have to do something with this?

Action and store callbacks are not executed in a single synchronous iteration

The code is something like this:

render: function() {
  return (
    <textarea
      value={this.props.value}
      onChange={this.handleChange}
    />
  );
},

handleChange: function(ev) {
  Actions.updateMessage(ev.target.value);
}

Also noticed this one, facebook/react#1698

Composed listenables fails in Opera

According to testling, the following test fails in Opera and older versions of Chrome:

not ok 8 Composed listenables should emit multiple times
  Error: Uncaught AssertionError: expected [] to deeply equal [ Array(2) ] (http://git.testling.com/work/spoike/repos/ba944a00a042aeeb2ec80d119d93684beb9ff196.1407304513039/1407304513279.c3212e4f.js:2319)
      at global.onerror (http://git.testling.com/work/spoike/repos/ba944a00a042aeeb2ec80d119d93684beb9ff196.1407304513039/node_modules/mocha/mocha.js:5871:10)

Joining parallel listeners to one

I'd like to propose an alternative to the waitFor function to something that makes more sense in a data flow pattern. As described with #18, there are occasions where you're interested in data coming in parallel. The API should be something as simple as a join function (name somewhat inspired by the parallel workflow concept):

var dataStore = Reflux.createStore({
    init: function() {
        this.listenTo(anAction, this.callback)
            .join(anotherAction, anotherStore /*, ...*/);
    },
    callback: function(anActionArgs, 
        anotherActionArgs, 
        anotherStoreArgs /* , ... */) {
            // ...
    }
});

It would be awesome if we didn't need to pull in a promises library dependency for this.

Please do let me know if we can improve this API before implementing it.

Reflux and FSM correlation

Just diving into React and of all the places I've seen this is likely the best place to ask my question. The mechanism I thought would work well for my app is a FSM, where the app functionality is contained in machine states that are transitioned between via UI events. Each state would have its own exchangeable model data that configures how it edits the main content model of the app.

My app is interaction oriented so I see FSM states/handlers as being the main drivers (instead of views), which in turn each decorate the UI with their own view additions. There is a main UI layer/component that the main FSM is attached to and transitioned from, then each unique FSM state may render its own further UI components and edit the main content model with its own actions.

What I'm reading about Flux and the improved Reflux sounds somewhat suitable for implementing a FSM? Do FSM states each related to separate stores and sets of actions? It would be great to learn how/if Reflux might be suited for a FSM control structure.

Asynchronous dataflow example

It would be great with an example on how to deal with asynchronous data sources along with optimistic updates in Reflux. Remote data is something everyone works with and something that Flux documents very poorly.

I suppose one way would be creating several events for each action (Todo.createItem, Todo.createItemSuccess and Todo.createItemFailure) and using the first to do optimistic updates.

bind methods in store

Methods in React components are automatically bound to the component instance, saving you from having to do any this.onClick = _.bind(this.onClick,this) stuff.

This is convenient and something you quickly get used to, so it feels like an annoyance not to have this convenience in Reflux stores. This crops up pretty often, as stores frequently need to use methods as callbacks for various databases, REST API:s etc.

So, question; should store methods be autobound to the instance?

Throw error when stores are chained in a circular loop

Story

As a developer using reflux
When I accidentally chain stores in a circular loop
Reflux.listenTo should throw an error notifying of the circular dependecy

Do some listener dependency tracking on actions and stores to make sure that developers don't accidentally do circular dependency and have an indication where it went wrong.

Still need waitFor?

I'm really liking in general the simplifications you've made over Facebook's Flux implementation. But one possible weakness with your approach of listening to stores vs. waiting for stores is stores lower in the hierarchy would lose access to the raw action data. This would be a problem if the top-level store only preserves a filtered view of the raw action data. A lower-level store could get around this to some degree by also listening to an action and storing its data and then later combining that with the higher-level store's data but then there could be problem ensuring matching up data.

So in short, I see value still in having a way of doing ordered event callbacks.

Add support for eventstream-like objects

Add support for listening to eventstream like objects. E.g. eventstreams from bacon.js or event-stream.

Under the hood we only need to check if the listenable object has a map function (which means also arrays may be listened to as well).

Motivating example

You will now be able to simply create eventstreams to be used as "automatic" actions or stores. E.g. resize event in bacon.js:

var resizeStream = $(window).asEventStream('resize');

var ResizeComponent = React.createClass({
    mixins: [Reflux.ListenerMixin],
    componentDidMount: function() {
        this.listenTo(resizeStream, this.onAppResize);
    },
    onAppResize: function() {
       // is called everytime the window resizes
    },
    render: function() { /* ... */ }
});

Thoughts?

Create convenience mixin to manage subscriptions

Each component needs to unsubscribe to avoid havoc. Having a component subscribe to multiple listenables only complicates the situation.

I propose to add a mixin which manages the subscriptions and takes care of unsubscribing. It may look similar to the following:

var ListenerMixin = {
  componentWillMount: function() {
    this.subscriptions = [];
  },

  listenTo: function(listenable, callback) {
    var unsubscribe = listenable.listen(callback, this);
    this.subscriptions.push(unsubscribe);
  },

  componentWillUnmount: function() {
    this.subscriptions.forEach(function(unsubscribe) {
      unsubscribe();
    });
  }
}

preEmit and shouldEmit in actions

Do we really need both preEmit and shouldEmit? Code from preEmit could just as easily be put inside shouldEmit instead.

I see the values of the explanatory names and not having to actively return true if you just want to run code pre-emission, but is that worth muddying the API?

As for having to return true to proceed, we could switch it so that emission is cancelled if return value is true.

Recommended way of initializing Store with data before first render?

I'm currently doing

myStore.update(data);
React.renderComponent(App, โ€ฆ)

where update is my custom method for updating store's internal list of objects and calling trigger.

I have to update store directly instead of using an action because actions fire asynchronously, and I wouldn't be able to use renderComponent's callback to check when app has finished rendering with data.

In App:

function getAppState() {
  return {
    myState: myStore.getState()
  };
}

...

getInitialState: getAppState,

_onChange: function() {
  this.setState(getAppState());
},

componentDidMount: function() {
  this.listenTo(myStore, this._onChange);
},

Any better way to go about this? Cheers.

Potential arguments leakage?

Saw this in another issue: pixijs/pixijs#853

Other sources on this:


May be relevant in reflux where arguments is potentially leaked. AFAIK, this is something for V8.

Points of interest:

  1. https://github.com/spoike/refluxjs/blob/ff67e1d996f44cf87e38d45a855f3b4291572d01/src/createStore.js
    Leak at line 44, which is caught in line 34. We shouldn't trust what happens in EventEmitter.emit.
  2. https://github.com/spoike/refluxjs/blob/ff67e1d996f44cf87e38d45a855f3b4291572d01/src/all.js#L58
    line 58: classic pattern. Use patch mentioned here: tj/node-thunkify#12

/cc @spoike @bripkens

Create multiple actions convenience function

Add to the Reflux API a way to easily create multiple actions to an object with an array of names as parameter. Example:

var Action = createActions([
    "activateGem",
    "deactivateGem",
    "openPopup",
    "closePopup"
  ]);

console.log(Action);
// Should output something like this:
// {
//     activateGem: {Action},
//     deactivateGem: {Action},
//     openPopup: {Action},
//     closePopup: {Action},
// }

Reflux.createActions needs more work?

PR at #13 is a baby step; which shares EventEmitter among group of actions.

This allows the possibility of certain features:

1. Should we bother adding/removing actions to/from context/namespace/group of actions?

May need to use Object.defineProperty for custom getter/setters to register/unregister to the namespace's EventEmitter. Since react.js supports IE8, Object.defineProperty will need to be polyfilled.

  1. Assign hook functions when calling actions. Hook functions get executed before/after each emit.

    Inspired by mount lifecycle of react.js.

    Proposed example:

    var Actions = Reflux.createActions([
      "statusUpdate", 
      "statusEdited",
      "statusAdded"
    ]);
    
    // assign hook functions
    
    // executed prior to emit
    Actions.statusUpdate.preEmit = function() {};
    
    // test whether emit should occur. Executes after preEmit.
    Actions.statusUpdate.shouldEmit = function() {};
    
    // Alternative syntactic sugar -- only assign preEmit functions
    
    var hooks = {};
    hooks.statusUpdate = funnction() {};
    hooks.statusEdited = funnction() {};
    
    // Map based on keys from hooks.
    var Actions = Reflux.createActions(hooks);
    
    // add/remove/change hook functions
    Actions.statusUpdate.shouldEmit = funnction() {};

Store ---> Hub?

Here's a curve ball; isn't "store" really a misnomer? It's not necessarily a store for anything. But if you do want to store something, it is a convenient place to deal with that.

What is it then? An event emission aggregator. I'm thinking node, or hub.

So, perhaps createHub? What do you think?

reflux and angular

Hey, i just wanted to start by saying that this library is really great. It's finally allowed me to really wrap my head around functional reactive programming and implement it easily. nice job.

I wanted to put some thoughts in here about implementing this in Angular and see what people might think about that. I'm using the angular-fullstack repo from DaftMonk as a starting point, and i have to say it's actually really great. It has allowed me to leverage the overall workflow of flux, the pub/sub aspect of of the stores and actions, sockets, and the 2way data-binding of directives.

So, you can create a Reflux factory, allowing you to trace all your reflux dependancies:

angular.module('refluxApp')
  .factory('RefluxService', function () {
    return Reflux;
  });

Then wrap your actions in angular factories as well:

.factory('ThingActions', function(RefluxService){
  return RefluxService.createActions([
      "thingRemove",
      "thingAdd",
      "thingUpdated",
      "thingLoad",
      "thingSocketSync",
      "thingSocketUnsync",
      ]);
})

And do the same for your Stores. You'll notice that i'm actually plugging this store into the socket.io service, and auto triggering events based on that. it works out quite well. I'm also auto loading this module after it's injected by using this.onLoad() in the init function. Sorry that this example is probably a little poorly constructed. it was just a proof of concept:

.factory('RefluxThingStoreService', function($http, socket, RefluxService, ThingActions){
    // Creates a DataStore
    return RefluxService.createStore({

        // Initial setup
        init: function() {

            // Register action
            this.listenTo(ThingActions.thingAdd, this.onAdd);
            this.listenTo(ThingActions.thingLoad, this.onLoad);
            this.listenTo(ThingActions.thingLoading, this.onLoading);
            this.listenTo(ThingActions.thingLoaded, this.onLoaded);
            this.listenTo(ThingActions.thingSocketSync, this.onSocketSync);

            this.things = [];

            this.loaded = false;

            this.onLoad();
          },

        onLoad:function() {
            if(this.loaded === false) {
                ThingActions.thingLoading();
            } else {
                console.log('already loaded')
            }
        },
        onLoading:function() {
            var ths = this;
            this.trigger('loading');
            api.get().success(function(awesomeThings){
                    _.each(awesomeThings, function(thing){
                        ths.things.push(thing);
                    });
                    ThingActions.thingSocketSync();
                    ThingActions.thingLoaded();

            });
        },
        onLoaded:function() {
            this.loaded = true;
            this.trigger('loaded');         
        },
        onSocketSync:function() {
            socket.syncUpdates('thing', this.things, function(event, item, array) {
                this.trigger(event, item)
            }.bind(this));
        },
        onSocketUnsync:function(){
            socket.unsyncUpdates('thing');
        }
    });

  })

So now i have three angular factories, one for Reflux, one for actions, and one for the datastore. What i believe to be great about this is that I can leverage Angular's dependency injection to track what directives, controllers, or services are using these.

It also becomes very easy to build state into my directives. One of the challenges i am trying to tackle is how to make UI components respond to the state/status of data as it flows through the system without modifying the actual data object. the streaming events situation seems to solve this.

here's an example of a directive that adds a progress box whenever a new thing is added.

  .directive('createThingProgress', function (RefluxService, ThingActions, RefluxThingStoreService) {
    return {
      restrict: 'EA',
      link: function (scope, element, attrs) {

        var Progress = ThingActions.thingAdd.listen(function(thing){
            var progressBar = angular.element('<div class="well progressBar"><h3>building '+thing.name+'</div>');
            element.append(progressBar);
            scope.$digest();
        });     

        var Completed = RefluxThingStoreService.listen(function(status, payload){
            if(status==='created') {
                var thing = element.children('.progressBar');
                thing.remove();
            }
            scope.$digest();
        });     

        scope.$on('$destroy', function(){
            Progress();
            Completed();
        });
      }
    };
  });

in this example, i'm using the listeners to stores and actions to modify the UI elements. but you can also leverage Angular's dirty checking to update data placed on the scope from a store.

Anyway, implementing things this way has been very successful for me on this initial test. I'm excited about exploring this framework more and interested to see what you guys think about this approach.

Tracking strategies for Reflux.all

As detailed in #28 by @bripkens. The following strategies exist:

  • trackLastCall (currently implemented in #28 for Reflux.all) - takes the last argument received for each
  • trackFirstCall - takes the first argument received for each
  • trackAllCalls - mashes all arguments together
  • ensureEqualNumberOfCalls - throws error if something was called more than once before the promise was completed

Revise API?

After looking at various flux implementations, I share some of @spoike's sentiments on certain design decisions that facebook adopts in their flux implementation (which every other flux implementation seems to copy).

I really like the idea that actions/stores are like functors. But I feel that its implementation and constructor via Reflux.createAction and Reflux.createActions doesn't seem right, especially for the fact that every action is basically an EventEmitter; which I think is overkill. I try to reconcile this by having Reflux.createActions share the EventEmitter instance among related actions.

Thus, I'm suggesting a revised API.

Rather than having each action become an EventEmitter, I think it makes more sense if they are instead "channels" of an EventEmitter. You can still create functors by wrapping around the associated emit function, which in this case becomes a one-to-many pub/sub topology.


I'd like some thoughts on the suggested revised API. The stores API is inspired by orchestrator used in gulp.js.

// revised Reflux API
var Reflux = require('Reflux');

/**
 * Actions
 */

var todoActions = new Reflux.Actions();

// shorthand
todoActions = new Reflux.Actions(['create', 'delete', 'edit']);

// shorthand for single action
todoActions = new Reflux.Actions('create');

// string array of unique action names
todoActions.add(['create', 'delete', 'edit']);
todoActions.add('create'); // shorthand

// alias for above
todoActions.register(['create', 'delete', 'edit']);

// should be rare to do this
todoActions.remove(['create', 'delete', 'edit']);
todoActions.remove('create'); // shorthand

todoActions.execute('create', args);
// alias for above
todoActions.emit('create', args);

// reference as a function (functor representation)
create = todoActions.get('create');

// alias for todoActions.execute('create', args);
create(args);

// with no args, get object with accessible functors as properties
actions = todoActions.get();
actions.create(args);

// shorthand for todoActions.get();, but doesn't accept any args
todoActions.list();
// output: ['create', 'delete', 'edit']


unlisten = todoActions.listen('create', handler);

// listen to functor
unlisten = create.listen(handler);

// hook functions.
todoActions.shouldEmit('create', handler);
todoActions.preEmit('create', handler);

// whenever a handler listens/unlistens to an action
todoActions.onListenerAdd('create', handler);
todoActions.onListenerRemove('create', handler);


// hook functions on functors
create.shouldEmit('create', handler);
create.preEmit('create', handler);

// whenever a handler listens/unlistens to an action
create.onListenerAdd(handler);
create.onListenerRemove(handler);

// misc
todoActions.removeAllListeners();
create.removeAllListeners();

// alias for todoActions.remove('create');
create.destroy();


/**
 * Stores
 */

var todoStore = new Reflux.Store();

/**
 * trigger - action or store for handler listen to
 * [dependencies] - array of action(s)/store(s) to wait on
 * handler - callback
 *
 * handler gets executed whenever trigger executes. However if dependencies are defined,
 * then whenver trigger executes, dependencies must be executed in their order before
 * handler is finally executed.
 *
 * returns destroy function that removes handler from trigger.
 *
 * TODO: should use promises with timeouts? (bluebird)
 * TODO: use such timeouts only in dev mode? (webpack, browserify, et al)
 */
destroy = todoStore.on(trigger, [dependencies], handler);

// [someAction, someStore] may be promises.
// destroy removes
todoStore.on(action, [someAction, someStore], function() {

    this.done('some result');

});

unlisten = todoStore.listen(handler);

// whenever a handler listens/unlistens to the store
todoStore.onListenerAdd(handler);
todoStore.onListenerRemove(handler);

// misc
todoStore.removeAllListeners();

Component reusability

Hi, how would one reuse a component using the (Re)flux architecture?

For instance the ToDo example, what if I would like to have multiple instances of ToDo lists on a page( Todo today, todo tomorrow etc). Because of the singleton nature of the Actions(dispatchers) all Todos would be updated on all actions triggered.

Cache actions and stores for easy introspection

Story

As a user
I want to find all created actions AND stores from Reflux module
So that I can: trigger actions AND/OR tap on what they emit in the browser console

Explanation

At the moment it is up to the web application to hold all the created actions and stores. This proposal is for creating a action and store cache that is exposed from e.g. Reflux._actions and Reflux._stores.

This makes it easier to do introspection from a browser console, and opens up the possibility to create browser extensions that can:

  • tap into what data the actions and stores do emit
  • create a map of actions and stores
  • easily trigger an action for development testing

Feature Request: Example with Server

Hey there,

Just came across this - it looks really cool, and I find the concepts behind this really intuitive, compared to some of the other Flux implementations out there. One thing that I'd find useful, and might be good to include in the docs, is how to integrate Reflux with fetching data from a server (or really, any other sort of asynchronous code).

Thanks!

Listen methods on actions have no context set

The listen method on actions have lost their this context after 0.1.8. Example code:

// Using a bacon stream to listen to an action:
var actionStream = Bacon.fromBinder(myAction.listen);

// will crash on 0.1.8

Workaround for now is to bind the action as context, example with lodash:

var actionStream = Bacon.fromBinder(_.bind(myAction.listen, myAction));

Create/fork TODO example app?

This project really interests me and I'm integrating it into my app.

It would be great, however, to demonstrate the "reflux way" using the the TODO app as an example. (perhaps just forking https://github.com/facebook/flux/blob/master/examples/flux-todomvc)

For example, I'm particularly interested in the best way to set initial state for components. In the Facebook TODO app example, they defined a function that gets called when the store emits its change event:

function getTodoState() {
  return {
    allTodos: TodoStore.getAll(),
    areAllComplete: TodoStore.areAllComplete()
  };
}

In the Reflux examples I've seen, state is passed passed directly to the callback rather than the component calling store methods. Of course the same thing could be done with Reflux but I'm not sure if that would be missing the point.

In short, it would be useful to see how the author of the project uses the library in a more detailed way with React.

Add callback for map/reduce capability on actions

From discussion in #57 it seems we may need to create actions together with a callback that works like the preEmit.

Add function as argument in createAction so you can do this:

var myAction = Reflux.createAction(function(arg) {
    console.log(arg);
    return arg + 1;
});

myAction.listen(function(arg) { console.log(arg); })

myAction("test");
// will output:
// test
// test1

Internally we could just pass the callback to the preEmit hook. Also the action needs to pass the return value preEmit does. By default, if preEmit does not return anything (i.e. returns undefined), the arguments are kept as is.

Creating multiple actions is done through an options object:

var Actions = Reflux.createActions({
    action1: true, // or any truthy value
    action2: function(arg) {
        console.log(arg);
        return arg + 1;
    }
});

Actions.action1(); // no preEmit hook, just a default action
Actions.action2("test"); // outputs test, passes on "test1"

Wrap action functions in setTimeout?

It's a common use case that action functors are normally tied to virtual DOM event callbacks within components in react.js. EventEmitters (node.js or 3rd-party) are not async in nature, unless otherwise designed to be.

Thus, whenever an emit function is called, it's done synchronously which begin from the action to the store (and chained stores).

Action functors should be executed in fire-and-forget fashion. Thus, I think we should wrap action functions in setTimeout with 0ms delay.

@spoike thoughts?

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.