Giter Club home page Giter Club logo

dispatchr's People

Contributors

alvieirajr avatar carystanley avatar dchest avatar evangoer avatar jedireza avatar kevintcoughlin avatar lingyan avatar mridgway avatar protron avatar redonkulus avatar szimek avatar vijar 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

dispatchr's Issues

Errors during action handlers

TL;DR If an error is thrown whilst handling an action the Dispatcher will not allow any further actions to be dispatched.

The logic that causes this is, I think, contained within Dispatcher::dispatch:

Dispatcher.prototype.dispatch = function dispatch(actionName, payload) {
    if (this.currentAction) {
        throw new Error('Cannot call dispatch while another dispatch is executing. Attempted to execute \'' + actionName + '\' but \'' + this.currentAction.name + '\' is already executing.');
    }
    ...
    this.currentAction = new Action(actionName, payload);
    ....
    this.currentAction.execute(handlerFns);
    debug('finished ' + this.currentAction.name);
    this.currentAction = null;

If an error is thrown during Action::execute, this.currentAction is never reset to null and so further calls to dispatch throw the 'Cannot call dispatch' error.

Using try around the Action::execute call prevents the 'Cannot call dispatch' error from being thrown under legitimate circumstances, in fact there is a test case ensuring that dispatch() does not swallow errors thrown by Store handlers.

Given that the current behaviour renders an app unusable, what are the suggestions for handling errors in a Fluxible app?

M

Bowerify the dispatchr

First off, thanks for open sourcing this! It has solved a number of problems I came across with the standard dispatcher as well as just making things much cleaner and easier to understand. That said, at this point the only way for me to use it, since I'm developing on top of rails and not node, is to copy the files over by hand into my source. It would be great if a bower.json file could be added and the code made workable for environments that don't use CommonJS. It seems like the two options are either a build task that converts the various libs into something consumable by the browser or making each file adaptable to either AMD, CommonJS, or the global object in the way EventEmitter does. Are either of these options feasible? I'd be happy to try and turn it into a PR if there's consensus on the way forward.

React errors caused by BaseStore emit do not reach the console

If an update to a Store creates a data condition that causes my react components to fail to render, the stacktrace is lost and does not make it to the error console.

I added the following to get me through but, I'm sure there's a cleaner way or maybe the developers have some further insight as to why this could be happening. But without the try/catch my react errors happen silently.

BaseStore.prototype.emitChange = function() {
    try {
        this.emit(CHANGE_EVENT, this.constructor);
    } catch (e) {
        console.error(e.stack);
    }
};

Investigate Synchronous Dispatching

More information on Flux shows that Facebook is using the dispatchers completely synchronously and handling I/O in separate actionCreator functions that are called pre-dispatch.

We should evaluate the benefits of this over handling async actions inside of stores.

export a function that returns Dispatcher

because currently module.exports = Dispatcher, it's not possible to make different dispatchers that register different stores in the same process. This is troublesome especially on the server side, where you might want to have multiple 'apps' running in the same process.

instead, if module.exports = function(){ var Dispatcher = function(){} /* etc etc */, return Dispatcher; } then one could make multiple dispatchers hooked up to different stores, depending on whatever.

Yes?

Throw an error if actionName passed to Dispatcher#dispatch is undefined

I'm using constants instead of strings for action names to have them all in a single place as a documentation. An additional benefit could be that if I make a typo in a name of a constant, thus passing undefined value, Dispatcher#dispatch could throw an error making it much easier to spot such typos.

What do you think?

Introduce store method `shouldDehydrate` to disable dehydration for a store

This method would allow stores to control whether they should dehydrate themselves on the server to be sent to the client. We have run into times where a store may have listened for an action, but we did not need the store to be rehydrated on the client. For instance, one-time-use server-only data.

In dispatchr's dehydrate method:

  • check if store implements shouldDehydrate method
    • if method exists, call it
      • if returns true, call dehydrate and return store's state
      • if returns false, ignore the store's state completely
    • if method does not exist:
      • always dehydrate (default to true)

Ability to have handler for all actions

With Facebook dispatcher, we can have a default: case in switch statement which will get executed for all the actions firing in the app. It is useful in the cases where developer wants do stating, logging etc. Even though this dispatcher makes registering process neat, we lose this ability which is useful in many cases

Promise support

It'd be nice if promises were baked into the API in such that any async operation could either return a promise or take a callback.

can emitChange from store support passing a paylod that holds an id

this could be just something really invalid, so consider this as a question if this is something that doesn't make sense to be implemented.

imagine i have a store, which holds a list of items. and i've an Item renderer component which renders each of these Items. Lets say each of the item component listens to store changes. with the current design, whenever one of the items changes in the store, all the instances of the item components will be re-renderered. assume if the store is able to attach an id in the emitChange call. so store says, i've a change, but only for item with id x. that would let the item renderer component to ignore the change notification if the change is not associated to it.

setImmediate shim as a dependency

This module requires a setImmediate shim to work in the browser and I didn't see it documented anywhere. Without debugging enabled in a webpack bundle, this was really annoying to track down. I think you should load the shim if needed at the top of the files it is used in.

Circular dependencies?

Just wondering can the implementation of waitFor() handles circular dependencies, for example, StoreA waits for StoreB and vice versa?

Why the Dispatcher don't return the class directly?

Current the Dispatcher module exports a function that returns the Dispatcher instead of returning the dispatcher class directly.

I think this is bad because every time that function is called it creates and returns a new class instead of using the same one, new objects create by those different classes will point to a different prototype object with methods that have exactly the same code being duplicated in memory...

Is there any reason why the lib/Dispatcher.js can't return the class directly?

Tie together multiple actions of one async operation

this is more of a question/proposal

For example if we have an autosuggest widget for the contacts that dispatches GET_CONTACTS_INVOKE and GET_CONTACTS_SUCCESS actions as user types the name of the contact.

as user is typing something in the input it'll dispatch multiple actions:
GET_CONTACTS_INVOKE
GET_CONTACTS_INVOKE
GET_CONTACTS_SUCCESS
GET_CONTACTS_SUCCESS

and there is no way of identifying which success corresponds to which invoke action.

Right now we're generating tokens and pass them with the payload

function actionCreator(context, payload) {
  var token = Math.random();
  context.dispatch('GET_CONTACTS_INVOKE', {token: token});
  apiClient.makeRequest().then(function(response) {
    context.dispatch('GET_CONTACTS_SUCCESS', {response: response, token: token});
  }).catch(function(error) {
    context.dispatch('GET_CONTACTS_FAILURE', {error: error, token: token});
  });
}

And on the stores side, we know how to group them together.

This is also important in the case of optimistic updates, because we need to know what we want to commit and what we want to rollback.

As an alternative and cleaner proposal, i was thinking, what if context.dispatch() had a pre-generated token in a closure every time the action creator function is invoked?
kind of like

function DispatcherContext (dispatcher, context) {
  /* ... */
  var token = Math.random();
  this.dispatch = function(actionName, payload) {
    return DispatcherContext.prototype.dispatch.call(this, actionName, payload, token);
  }; 
}

so this action creator invoked twice will produce:

function actionCreator(context, payload) {
  context.dispatch('INVOKE');
  async().then(function(response) {
    context.dispatch('SUCCESS', {response: response});
  });
};

will produce

// => 'INVOKE', null, 123456
// => 'INVOKE', null, 098765
// => 'SUCCESS', {response: {}}, 098765
// => 'SUCCESS', {response: {}}, 123456

What are you thoughts on that?

Encourage passing constructors to getStore and waitFor instead of strings

For dependency mapping, it's better to require a store and pass the constructor to getStore and waitFor instead of store name string. At the very least, the readme should be updated to reflect this and use constructors instead of strings in the examples.

It may also be better to remove string capabilities in getStore and create a separate function getStoreByName for accessing stores by name.

Investigate replay-based rehydration

Theoretically, stores could be reset to their original state by recording the actions and payloads that were dispatched on the server, serializing them, and then replaying them on the client to rehydrate the application. An investigation on the performance and implementation benefits for each should be done.

This could reduce the amount of code in each store since they would no longer need to implement dehydrate/rehydrate functions.

Handler definitions as functions to promote a sort of Revealing Module Pattern

Currently Stores look like the following:

module.exports = createStore({
    statics: {
        storeName: 'SomeStore',
        handlers: {
            'SOME_MUTATION': 'some_mutation',
        }
    },

    some_mutation: function(state) {
        this.state.count++;
        this.emitChage();
    },

    initialize: function() {
        ...
    },

    getState: function() {
        return this.state;
    }
});

The problem here is that the store "mutation" functions are still accessible, a way that would help hide direct mutation calls would be to allow the "handlers" hash to accept functions instead of just method name strings

example:

module.exports = createStore({
    statics: {
        storeName: 'SomeStore',
        handlers: {
            'SOME_MUTATION': function(state) {
                this.state.count++;
                this.emitChage();
            }
        }
    },

    initialize: function() {
        ...
    },

    getState: function() {
        return this.state;
    }
});

This limits mutations to not be directly accessible on the store object, but only thought the action handlers, of course the store would be bound to "this" in the handler functions.

This would help separate accessor and mutator functions in stores.

Unable to affect state asynchronously from store, apply component `this` to handler function?

Within a react native app, I do not see a way to apply the component this context to the handler callback function.

// ExampleStore.js - store
'use strict';

var createStore = require('dispatchr/addons/createStore');
var Promise = require('bluebird');

var ExampleStore = createStore({
  storeName: 'ExampleStore',
  handlers: {
    GET_STATE: function () {
      var endpoint = '/endpoint';

      var promise = fetch(endpoint)
        .then(response => response.json());

      return Promise.resolve(promise)
        .then((data) => {
          // this.state is undefined...
          this.state.hasData = true;
          this.state.data = data;
          this.emitChange();
          // ...but can't access component setState(), either.
          this.setState({
            hasData: true,
            data: data
          });
        })
        .done();
    }
  }
});

module.exports = ExampleStore;

// Example.js - component
'use strict';

var React = require('react-native');
var ExampleStore = require('../../stores/ExampleStore');
var dispatcher = require('dispatchr').createDispatcher({
  stores: [ExampleStore]
});
var contextOptions = {};
var dispatcherContext = dispatcher.createContext(contextOptions);
var { View } = React;

var Example = React.createClass({
  getInitialState: function () {
    return {
      hasData: false,
      data: {}
    };
  },

  componentDidMount: function () {
    dispatcherContext.dispatch('GET_STATE', {});
  },

  render: function () {
    return (
      <View></View>
    );
  }
});

module.exports = Example;

It seems it may be desirable to have a way to apply the component this here - https://github.com/yahoo/dispatchr/blob/master/lib/Action.js#L67

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.