npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fflux

v0.14.4

Published

Flux-based library to provide some easy API to build architecture

Downloads

36

Readme

fflux.js [deprecated, use redux instead]

Build Status Code Climate Test Coverage

Contents:

Examples:

What is FFlux?

  • Simple way to use flux architecture
  • Minimum API
  • Two types of stores: mutable & immutable
  • Isomorphic friendly (no singletons)
  • 100% test coverage
  • Detailed error messages (using facebook's invariant)
  • Modular: use only parts you need

Roadmap

  • [ ] Finalize the API
  • [X] Create a data scope for simple data (de|re)hydration
  • [X] Create application abstraction
  • [X] Create action scope abstraction
  • [X] Separate stores to mutable and immutable
  • [ ] Write "Getting Started"
  • [X] Make an example of isomorphic app
  • [ ] Make an example for Riot.js
  • [ ] Find a way to avoid using mixins

Installation

npm

npm install fflux

bower

bower install fflux

Application

Application is an entry point for fflux.

var Application = require('fflux/src/Application');

var app = new Application();

Also, you can use config object to pre-set application enviroment:


var app = new Application({
    stores: {
        list: new ListStore(),
        wishlist: new WishlistStore()
    },
    actions: {
        list: listActions,
        wishlist: wishlistActions
    }
});

Once you got app instance, you can use it to access storesScope and actionsScope by using app.stores() or app.actions(). Also you're able to get access to specific store or action directly: app.stores('some') or app.actions('some').

Dispatcher

FFlux dispatcher extends facebook's dispatcher implementation.

var Dispatcher = require('fflux/src/Dispatcher');
var dispatcher = new Dispatcher();

dispatcher.dispatch('SOME_ACTION', payload);

Dispatcher API

  • register - register store in dispatcher After registration, store will receive dispatcher's actions

    dispatcher.register(store);
  • unregister - unregister store from the dispatcher Store wouldn't receive actions any more.

    dispatcher.unregister(store);
  • dispatch - dispatch action to the stores:

    dispatcher.dispatch(actionName, actionPayload);`
  • waitFor - wait for another store to process action first

    var someStore = FFlux.ImmutableStore();
    
    dispatcher.waitFor([someStore]);

Action Creators

Action Creators are commonly used to fetch/post data. All async stuff should happend here.

var request = require('superagent');

var ActionCreatorExample = {
  /**
   * Fetch data from server
   * @param {Function} dispatch
   * @param {Object} criteria
   */
  fetchData: function(dispatch, criteria) {
    request
      .get('/some/url')
      .end(function(res) {
        dispatch('FETCH_DATA', res);
      });
  },

  /*
   * Post data to the server
   * @param {Function} dispatch
   * @param {Object} data
   */
  postData: function(dispatch, data) {
    request
      .post('/some/url')
      .send(data)
      .end(function(err, res) {
        if (err) {
          dispatch('FETCH_DATA_ERROR', err);
        }
        dispatch('POST_DATA', res);
      });
  }
};

In this example I use superagent as isomorphic library for AJAX calls. As you probably mentioned, in every function we got dispatch as a first parameter. That's done to simplify your life. Every time you register action creator in the action scope, this dependency injection managed for you automaticly.

Action Scope

The point is, that as like with the stores, it's handy to keep all your action creators inside your application instance. So, to provide you consistant interface (with data scope), action scope has been created. Action Scope isn't aimed to be used outside from the application

var app = new Application({
  actions: {
    scope: scopeActions
  }
});

app.actions(); // ActionScope instance

Action Scope API

  • register - register action creator object in the scope

    app.actions().register('otherScope', otherScopeActions);
  • unregister - unregister action creator object by name

    app.actions().unregister('otherScope');
  • get - register action creator object in the scope

    app.actions().get('scope'); // scope action creator

Stores

In fflux, you can use mutable and immutable stores. If you want to work with store's state as native javascript object - you should use mutable store. If you prefer immutable structures, immutable stores - is your choice. Both stores have the same API:

Store API

  • setState - merge current state with the one provided Hint: if you're using mutable stores, every setState will emit change event. In the case of immutable stores, change event will be emitted only if new state is different from the current one.

    store.setState({ key: value });
  • replaceState - replace current state with a given one

    store.replaceState({ key: value });
  • registerAction - register action handler

    store.registerAction(actionName, actionHandler);
  • unregisterAction - unregister action handler

    store.unregisterAction(actionName);

Mutable Store

Mutable store is a basic store you have in fflux:


var store = new FFlux.MutableStore({
  /**
   * In this property we declare list
   * of the actions we're interested in.
   */
  actions: {
    'SOME_ACTION': 'someMethod'
    'OTHER_ACTION': 'otherActionHandler'
  },

  /**
   * Get initial state
   * Works the same way as in React
   * @return {Any} Initial state
   */
  getInitialState: function() {
    return {};
  }

  /**
   * Handler for SOME_ACTION
   * @param {Object} data Payload
   * @return {Void}
   */
  someActionHandler: function(data) {
    this.setState(data);
  },

  /**
   * Handler for OTHER_ACTION
   * @param {Object} data Payload
   * @return {Void}
   */
  otherActionHandler: function(data) {
    this.setState(data);
  }
});

Every handler could be a method name of the store instance or a standalone function. In every action handler you can use waitFor method as described here:

{
  /**
   * Some action's handler
   * @param {Object} data Payload
   * @return {Void}
   *
   * @description For invoke some function(s) only *after* other store
   * process action, we need to use `waitFor` method
   */
  someMethod: function(data) {
    /**
     * If we need to be sure, that some function will be called
     * only after `storage` store would process the action
     */
    dispatcher.waitFor([storage]);
  }
}

You can register/unregister action handlers dynamically after store initialization:

var store = new FFlux.MutableStore({...});

/**
 * Action handler function
 * @param {Object} data Payload
 * @return {Void}
 */
function actionHandler(data) {
  //...
}

/**
 * Register handler `actionHandler` for action `SOME_ACTION`
 */
store.registerAction('SOME_ACTION', actionHandler);

/**
 * Call `unregisterAction` for remove action handler
 */
store.unregisterAction('SOME_ACTION');

And the last, but not least: states. FFlux stores have a state like React components, so you can easily work with it using already familiar functions: setState, getState, replaceState and getInitialState.

Immutable store

Immutable store inherits form mutable store and enhances its functionality with immutable data.

var store = new FFlux.ImmutableStore();

/*
 * currentState will be empty Immutable.Map
 */
var currentState = store.getState();

/**
 * Mutator for `c` key in `a.b.c` path
 * @param  {Array} element
 * @return {Immutable.List}
 */
function mutator(element) {
    return element.map(function(i) {
      return i * i;
    });
  }
}

/*
 * If we using immutable data,
 * any manipulation with `currentState`
 * will create a new immutable object
 */
var newState = currentState
  .updateIn(['a', 'b', 'c'], mutator)
  .set('b', 'new key')
  .set('c', currentState.getIn(['a', 'b', 'c']));

// currentState is still the same (empty Immutable.Map)

store.replaceState(newState);

Any store state operation (e.g. setState or replaceState) will trigger change event only in the cases when previous state isn't equal to the new one.

Data Scope

Data Scope is used in the fflux applications for grouping stores and providing simple interface to manage them. For example, it's very handy for building isomorphic applications, when you need to serialize and deserialize all your stores to transmit them from server to client.

var app = new Application({
  stores: {
    someStore: someStore
  }
});

Also, if you're using data scope with Application, all stores you add to data scope would be automaticly registered at dispatcher (if they weren't registered before).

Data Scope API

  • register - register store in the scope

    app.stores().register('someStore', someStore);
  • get - get registered store by name

    app.stores().get('someStore');
  • unregister - unregister store from the scope

    app.stores().unregister('someStore');
  • dehydrate - stringify store's state

    var dataString = app.stores().dehydrate();
  • rehydrate - fill store with pre-served data

    app.stores().rehydrate(dataString);

Once you registered all of your stores in the scope, you can simply dehydrate it and get a data string, using which you can rehydrate scope back in the future. It's especially handy when you're pre-fetching data on the back-end and want to transmit it to front-end.

For further reading check this example.

View layer

FFlux is view-agnostic. The only thing that fflux provides you - a mixin for react to bind to store, which would add a storeDidUpdate function to handle store's update:

/**
 * Create some immutable store
 */
var store = new FFlux.ImmutableStore({...});

/**
 * React class to describe component
 */
var MyComponentClass = React.createClass({
  /**
   * Bind React view to listen `change` event of the `store`
   * @type {Array}
   */
  mixins: [FFlux.mixins.bind(store)],

  getInitialState: function() {
    return store.getState();
  },

  /**
   * After store emit `change` event
   * this function will be invoked
   * @return {Void}
   */
  storeDidUpdate: function() {
    this.setState(store.getState());
  },

  render: function() {
    var state = this.getState();
    return (
      <div>{state.get('something')}</div>
    );
  }
});

/**
 * That's it, now you can render `MyComponent` and
 * as soon as `store` will emit `change` event,
 * your component will be redrawn
 */
React.render(<MyComponent />, document.body);