GithubHelp home page GithubHelp logo

hhy5277 / refluxjs Goto Github PK

View Code? Open in Web Editor NEW

This project forked from reflux/refluxjs

0.0 2.0 0.0 927 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 Introduction

RefluxJS

A simple library for unidirectional dataflow architecture inspired by ReactJS Flux.

NPM Version NPM Downloads Bower Version Dependencies Build Status Gratipay

Sauce Test Status

Development version: 0.5.x (release notes)

Note

Hello! Version 0.5.0 is in the works, and this readme reflects the progress on that upcoming version– particularly the section on React ES6 Usage. If you're looking for the readme for the most recent release, see v0.4.1 here.

You can read an overview of Flux here, however the gist of it is to introduce a more functional programming style architecture by eschewing MVC like pattern and adopting a single data flow pattern.

╔═════════╗       ╔════════╗       ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝       ╚════════╝       ╚═════════════════╝
     ^                                      │
     └──────────────────────────────────────┘

The pattern is composed of actions and data stores, where actions initiate new data to pass through data stores before coming back to the view components again. If a view component has an event that needs to make a change in the application's data stores, they need to do so by signaling to the stores through the actions available.

Feel free to open an issue on our discussion forum for questions and general discussion. Here is a complete list of communication channels:

  1. The discussion forum
  2. StackOverflow with the refluxjs tag
  3. #flux channel on Reactiflux Discord group. Sign up here for an account.
  4. Gitter
  5. Thinkful

Please use the issue tracker only for bugs and feature requests.

If you don't want to use the React-specific API, or want to develop Reflux for your view engine framework of choice, have a look at reflux-core.

Content

Comparing RefluxJS with Facebook Flux

The goal of the refluxjs project is to get this architecture easily up and running in your web application, both client-side or server-side. There are some differences between how this project works and how Facebook's proposed Flux architecture works:

You can read more in this blog post about React Flux vs Reflux.

Similarities with Flux

Some concepts are still in Reflux in comparison with Flux:

  • There are actions
  • There are data stores
  • The data flow is unidirectional

Differences with Flux

Reflux has refactored Flux to be a bit more dynamic and be more Functional Reactive Programming (FRP) friendly:

  • The singleton dispatcher is removed in favor for letting every action act as dispatcher instead.
  • Because actions are listenable, the stores may listen to them. Stores don't need to have big switch statements that do static type checking (of action types) with strings
  • Stores may listen to other stores, i.e. it is possible to create stores that can aggregate data further, similar to a map/reduce.
  • waitFor is replaced in favor to handle serial and parallel data flows:
  • Aggregate data stores (mentioned above) may listen to other stores in serial
  • Joins for joining listeners in parallel
  • Action creators are not needed because RefluxJS actions are functions that will pass on the payload they receive to anyone listening to them

Back to top

Examples

You can find some example projects at these locations:

Back to top

Extensions and Plugins

Installation

You can currently install the package as a npm package or bower component.

NPM

The following command installs reflux as a npm package:

npm install reflux

Bower

The following command installs reflux as a bower component that can be used in the browser:

bower install reflux

CDN

Reflux is available at jsdelivr.

ES5

Like React, Reflux depends on an es5-shim for older browsers. The es5-shim.js from kriskowal's es5-shim provides everything required.

Back to top

Usage

Creating actions

Create an action by calling Reflux.createAction with an optional options object.

var statusUpdate = Reflux.createAction(options);

An action is a function object that can be invoked like any function.

statusUpdate(data); // Invokes the action statusUpdate
statusUpdate.triggerAsync(data); // same effect as above

If options.sync is true, the functor will instead call action.trigger which is a synchronous operation. You can change action.sync during the lifetime of the action, and the following calls will honour that change.

There is also a convenience function for creating multiple actions.

var Actions = Reflux.createActions([
    "statusUpdate",
    "statusEdited",
    "statusAdded"
  ]);

// Actions object now contains the actions
// with the names given in the array above
// that may be invoked as usual

Actions.statusUpdate();

Asynchronous actions

For actions that represent asynchronous operations (e.g. API calls), a few separate dataflows result from the operation. In the most typical case, we consider completion and failure of the operation. To create related actions for these dataflows, which you can then access as attributes, use options.children.

// this creates 'load', 'load.completed' and 'load.failed'
var Actions = Reflux.createActions({
    "load": {children: ["completed","failed"]}
});

// when 'load' is triggered, call async operation and trigger related actions
Actions.load.listen( function() {
    // By default, the listener is bound to the action
    // so we can access child actions using 'this'
    someAsyncOperation()
        .then( this.completed )
        .catch( this.failed );
});

There is a shorthand to define the completed and failed actions in the typical case: options.asyncResult. The following are equivalent:

createAction({
    children: ["progressed","completed","failed"]
});

createAction({
    asyncResult: true,
    children: ["progressed"]
});

Action hooks

There are a couple of hooks available for each action.

  • preEmit - Is called before the action emits an event. It receives the arguments from the action invocation. If it returns something other than undefined, that will be used as arguments for shouldEmit and subsequent emission.

  • shouldEmit - Is called after preEmit and before the action emits an event. By default it returns true which will let the action emit the event. You may override this if you need to check the arguments that the action receives and see if it needs to emit the event.

Example usage:

Actions.statusUpdate.preEmit = function() { console.log(arguments); };
Actions.statusUpdate.shouldEmit = function(value) {
    return value > 0;
};

Actions.statusUpdate(0);
Actions.statusUpdate(1);
// Should output: 1

You can also set the hooks by sending them in a definition object as you create the action:

var action = Reflux.createAction({
    preEmit: function(){...},
    shouldEmit: function(){...}
});

Reflux.ActionMethods

If you would like to have a common set of methods available to all actions you can extend the Reflux.ActionMethods object, which is mixed into the actions when they are created.

Example usage:

Reflux.ActionMethods.exampleMethod = function() { console.log(arguments); };

Actions.statusUpdate.exampleMethod('arg1');
// Should output: 'arg1'

Back to top

Creating data stores

Create a data store much like ReactJS's own React.createClass by passing a definition object to Reflux.createStore. You may set up all action listeners in the init function and register them by calling the store's own listenTo function.

// Creates a DataStore
var statusStore = Reflux.createStore({

    // Initial setup
    init: function() {

        // Register statusUpdate action
        this.listenTo(statusUpdate, this.output);
    },

    // Callback
    output: function(flag) {
        var status = flag ? 'ONLINE' : 'OFFLINE';

        // Pass on to listeners
        this.trigger(status);
    }

});

In the above example, whenever the action is called, the store's output callback will be called with whatever parameters were sent in the action. E.g. if the action is called as statusUpdate(true) then the flag argument in output function is true.

A data store is a publisher much like the actions, so they too have the preEmit and shouldEmit hooks.

Reflux.StoreMethods

If you would like to have a common set of methods available to all stores you can extend the Reflux.StoreMethods object, which is mixed into the stores when they are created.

Example usage:

Reflux.StoreMethods.exampleMethod = function() { console.log(arguments); };

statusStore.exampleMethod('arg1');
// Should output: 'arg1'

Mixins in stores

Just as you can add mixins to React components, so it is possible to add your mixins to Store.

var MyMixin = { foo: function() { console.log('bar!'); } }
var Store = Reflux.createStore({
    mixins: [MyMixin]
});
Store.foo(); // outputs "bar!" to console

Methods from mixins are available as well as the methods declared in the Store. So it's possible to access store's this from mixin, or methods of mixin from methods of store:

var MyMixin = { mixinMethod: function() { console.log(this.foo); } }
var Store = Reflux.createStore({
    mixins: [MyMixin],
    foo: 'bar!',
    storeMethod: function() {
        this.mixinMethod(); // outputs "bar!" to console
    }
});

A nice feature of mixins is that if a store is using multiple mixins and several mixins define the same lifecycle method (e.g. init, preEmit, shouldEmit), all of the lifecycle methods are guaranteed to be called.

Listening to many actions at once

Since it is a very common pattern to listen to all actions from a createActions call in a store init call, the store has a listenToMany function that takes an object of listenables. Instead of doing this:

var actions = Reflux.createActions(["fireBall","magicMissile"]);

var Store = Reflux.createStore({
    init: function() {
        this.listenTo(actions.fireBall,this.onFireBall);
        this.listenTo(actions.magicMissile,this.onMagicMissile);
    },
    onFireBall: function(){
        // whoooosh!
    },
    onMagicMissile: function(){
        // bzzzzapp!
    }
});

...you can do this:

var actions = Reflux.createActions(["fireBall","magicMissile"]);

var Store = Reflux.createStore({
    init: function() {
        this.listenToMany(actions);
    },
    onFireBall: function(){
        // whoooosh!
    },
    onMagicMissile: function(){
        // bzzzzapp!
    }
});

This will add listeners to all actions actionName who have a corresponding onActionName (or actionName if you prefer) method in the store. Thus if the actions object should also have included an iceShard spell, that would simply be ignored.

The listenables shorthand

To make things more convenient still, if you give an object of actions to the listenables property of the store definition, that will be automatically passed to listenToMany. So the above example can be simplified even further:

var actions = Reflux.createActions(["fireBall","magicMissile"]);

var Store = Reflux.createStore({
    listenables: actions,
    onFireBall: function(){
        // whoooosh!
    },
    onMagicMissile: function(){
        // bzzzzapp!
    }
});

The listenables property can also be an array of such objects, in which case all of them will be sent to listenToMany. This allows you to do convenient things like this:

var Store = Reflux.createStore({
    listenables: [require('./darkspells'),require('./lightspells'),{healthChange:require('./healthstore')}],
    // rest redacted
});

Listenables and asynchronous actions

If options.children is set, as in the example below, you can use onActionSubaction to add a listener to the child action. For example:

var Actions = Reflux.createActions({
    "load": {children: ["completed", "failed"]}
});

function handleLoad(Action, Subaction){
    console.log("The on" + Action + Subaction + " handler was called");
};

var Store = Reflux.createStore({
    listenables: Actions,
    onLoad: function() {
        handleLoad("Load");
    },
    onLoadCompleted: function() {
        handleLoad("Load", "Completed");
    },
    onLoadFailed: function() {
        handleLoad("Load", "Failed");
    }
});

Listening to changes in data store

In your component, register to listen to changes in your data store like this:

// Fairly simple view component that outputs to console
function ConsoleComponent() {

    // Registers a console logging callback to the statusStore updates
    statusStore.listen(function(status) {
        console.log('status: ', status);
    });
};

var consoleComponent = new ConsoleComponent();

Invoke actions as if they were functions:

statusUpdate(true);
statusUpdate(false);

With the setup above this will output the following in the console:

status:  ONLINE
status:  OFFLINE

Back to top

React component example

Register your component to listen for changes in your data stores, preferably in the componentDidMount lifecycle method and unregister in the componentWillUnmount, like this:

var Status = React.createClass({
    getInitialState: function() { },
    onStatusChange: function(status) {
        this.setState({
            currentStatus: status
        });
    },
    componentDidMount: function() {
        this.unsubscribe = statusStore.listen(this.onStatusChange);
    },
    componentWillUnmount: function() {
        this.unsubscribe();
    },
    render: function() {
        // render specifics
    }
});

It's also important to note that Reflux now supports React ES6 style usage as well.

Convenience mixin for React

You always need to unsubscribe components from observed actions and stores upon unmounting. To simplify this process you can use mixins in React. There is a convenience mixin available at Reflux.ListenerMixin. Using that, the above example can be written like thus:

var Status = React.createClass({
    mixins: [Reflux.ListenerMixin],
    onStatusChange: function(status) {
        this.setState({
            currentStatus: status
        });
    },
    componentDidMount: function() {
        this.listenTo(statusStore, this.onStatusChange);
    },
    render: function() {
        // render specifics
    }
});

The mixin provides the listenTo method for the React component, that works much like the one found in the Reflux's stores, and handles the listeners during mount and unmount for you. You also get the same listenToMany method as the store has.

Using Reflux.listenTo

If you're not reliant on any special logic for the this.listenTo calls inside componentDidMount, you can instead use a call to Reflux.listenTo as a mixin. That will automatically set up the componentDidMount and the rest for you, as well as add the ListenerMixin functionality. With this our example above can be reduced even further:

var Status = React.createClass({
    mixins: [Reflux.listenTo(statusStore,"onStatusChange")],
    onStatusChange: function(status) {
        this.setState({
            currentStatus: status
        });
    },
    render: function() {
        // render using `this.state.currentStatus`
    }
});

You can have multiple calls to Reflux.listenTo in the same mixins array.

There is also Reflux.listenToMany which works in exactly the same way, exposing listener.listenToMany.

Using Reflux.connect

If all you want to do is update the state of your component to whatever the data store transmits, you can use Reflux.connect(listener,stateKey) as a mixin. The state is updated via this.setState({<stateKey>:data}). Here's the example above changed to use this syntax:

var Status = React.createClass({
    mixins: [Reflux.connect(statusStore,"currentStatus")],
    render: function() {
        // render using `this.state.currentStatus`
    }
});

The Reflux.connect() mixin will check the store for a getInitialState method. If found it will set the components getInitialState

var statusStore = Reflux.createStore({
    getInitialState: function() {
        return "open";
    }
});

var Status = React.createClass({
    mixins: [Reflux.connect(statusStore,"currentStatus")],
    render: function() {
        // render using `this.state.currentStatus`
        // this.state.currentStatus === "open"
    }
});

Using Reflux.connectFilter

Reflux.connectFilter is used in a similar manner to Reflux.connect. Use the connectFilter mixin when you want only a subset of the items in a store. A blog written using Reflux would probably have a store with all posts in it. For an individual post page, you could use Reflux.connectFilter to filter the posts to the post that's being viewed.

var PostView = React.createClass({
    mixins: [Reflux.connectFilter(postStore, "post", function(posts) {
        return posts.filter(function(post) {
           return post.id === this.props.id;
        }.bind(this))[0];
    })],
    render: function() {
        // render using `this.state.post`
    }
});

Listening to changes in other data stores (aggregate data stores)

A store may listen to another store's change, making it possible to safely chain stores for aggregated data without affecting other parts of the application. A store may listen to other stores using the same listenTo function as with actions:

// Creates a DataStore that listens to statusStore
var statusHistoryStore = Reflux.createStore({
    init: function() {

        // Register statusStore's changes
        this.listenTo(statusStore, this.output);

        this.history = [];
    },

    // Callback
    output: function(statusString) {
        this.history.push({
            date: new Date(),
            status: statusString
        });
        // Pass the data on to listeners
        this.trigger(this.history);
    }

});

Back to top

Advanced usage

Switching EventEmitter

Don't like to use the EventEmitter provided? You can switch to another one, such as NodeJS's own like this:

// Do this before creating actions or stores

Reflux.setEventEmitter(require('events').EventEmitter);

Switching nextTick

Whenever action functors are called, they return immediately through the use of setTimeout (nextTick function) internally.

You may switch out for your favorite setTimeout, nextTick, setImmediate, et al implementation:

// node.js env
Reflux.nextTick(process.nextTick);

For better alternative to setTimeout, you may opt to use the setImmediate polyfill, setImmediate2 or macrotask.

Joining parallel listeners with composed listenables

The Reflux API contains join methods that makes it easy to aggregate publishers that emit events in parallel. This corresponds to the waitFor method in Flux.

Argument tracking

A join is triggered once all participating publishers have emitted at least once. The callback will be called with the data from the various emissions, in the same order as the publishers were listed when the join was created.

There are four join methods, each representing a different strategy to track the emission data:

  • joinLeading: Only the first emission from each publisher is saved. Subsequent emissions by the same publisher before all others are finished are ignored.
  • joinTrailing: If a publisher triggers twice, the second emission overwrites the first.
  • joinConcat: An array of emission arguments are stored for each publisher.
  • joinStrict: An error is thrown if a publisher emits twice before the join is completed.

The method signatures all look like this:

joinXyz(...publisher,callback)

Once a join is triggered it will reset, and thus it can trigger again when all publishers have emitted anew.

Using the listener instance methods

All objects using the listener API (stores, React components using ListenerMixin, or other components using the ListenerMethods) gain access to the four join instance methods, named after the argument strategy. Here's an example saving the last emission from each publisher:

var gainHeroBadgeStore = Reflux.createStore({
    init: function() {
        this.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData, this.triggerAsync);
    }
});

actions.disarmBomb("warehouse");
actions.recoverData("seedyletter");
actions.disarmBomb("docks");
actions.saveHostage("offices",3);
// `gainHeroBadgeStore` will now asynchronously trigger `[["docks"],["offices",3],["seedyletter"]]`.

Using the static methods

Since it is rather common to have a store where the only purpose is to listen to a join and trigger when the join is completed, the join methods have static counterparts on the Reflux object which return stores listening to the requested join. Using them, the store in the example above could instead be created like this:

var gainHeroBadgeStore = Reflux.joinTrailing(actions.disarmBomb, actions.saveHostage, actions.recoverData);

Sending initial state with the listenTo function

The listenTo function provided by the Store and the ListenerMixin has a third parameter that accepts a callback. This callback will be invoked when the listener is registered with whatever the getInitialState is returning.

var exampleStore = Reflux.createStore({
    init: function() {},
    getInitialState: function() {
        return "the initial data";
    }
});

// Anything that will listen to the example store
this.listenTo(exampleStore, onChangeCallback, initialCallback)

// initialCallback will be invoked immediately with "the initial data" as first argument

Remember the listenToMany method? In case you use that with other stores, it supports getInitialState. That data is sent to the normal listening callback, or a this.on<Listenablename>Default method if that exists.

Back to top

React ES6 Usage

React ES6 component example

Reflux exposes Reflux.Component for class extension for easy creation of ES6 style React components that automatically has the state of one or more Reflux stores mixed into the React component state. In order to accomplish this you simply need use Reflux stores that start with a state property with an object holding the default state of the store's data (i.e. set this.state = {my:"defaults"} in the store's init) , then you need to set this.store (to 1 store) or this.stores (to an Array of stores) from within the constructor of the component. An example would look like this:

class MyComponent extends Reflux.Component // <- Reflux.Component instead of React.Component
{
    constructor(props) {
        super(props);
        this.state = {foo:'bar'}; // <- stays usable, so normal state usage can still happen
        this.store = myStore; // <- the only thing needed to tie the store into this component
    }

    render() {
        // `storeProp` is mixed in from the store, and reflects in the component state
        return <p>From Store: {this.state.storeProp}, Foo: {this.state.foo}</p>;
    }
}

The default states of the stores will be mixed in from the start, and any time the store does a trigger the triggered data will be mixed in to the component and it will re-render. If you wish to avoid too many root properties in the state then you can just namespace your store's state to avoid that (i.e. your store's state looks like this.state.mycounter.count instead of just this.state.count).

A fully working example may look something like this:

var Actions = Reflux.createActions(["increment"]);

var counterStore = Reflux.createStore(
{
    listenables: Actions,
    
    init: function() {
        this.state = {count:0};
    },
    
    onIncrement: function(txt) {
        this.state.count++;
        this.trigger(this.state);
    }
});

class Counter extends Reflux.Component
{
    constructor(props) {
        super(props);
        this.state = {};
        this.store = counterStore;
    }
    
    render() {
        return <p>Count: {this.state.count}</p>;
    }
}


ReactDOM.render(
    <Counter />,
    document.getElementById('container')
);

setInterval(Actions.increment, 1000);

Using ES6 Reflux Stores via Reflux.Store

Stores do not directly integrate within React like Reflux.Component needs to, so using a more idiomatic way to declare them is not necessary. However, it can be very useful when using the Reflux.Component style components. Therefore whenever Reflux.Component is exposed Reflux also exposes Reflux.Store which can be extended to make a class that wraps and acts as a reflux store but with an approach that is easier to implement into Reflux.Component classes.

To create one looks something like this:

class MyStore extends Reflux.Store
{
	constructor() {
		this.state = {foo:'bar'}; // <-- the store's default state
	}
}

These act much like a normal store. You can use this.listenTo, this.listenToMany, etc. from within the constructor, and you can define things like a this.listenables property and it will automatically call action and onAction named methods on the class. It also exposes a setState method that you can use to modify your state property and automatically trigger the change:

var Actions = Reflux.createActions(["increment"]);

class CounterStore extends Reflux.Store
{
	constructor() {
		this.listenables = Actions;
		this.state = {count:0};
	}
	
	onIncrement() {
		var cnt = this.state.count;
		this.setState({count:cnt+1});
	}
}

One thing you may notice is that the original style Reflux.createStore creates an actual instance (as opposed to a class) which is what is assigned to this.store in the Reflux.Component. Extending Reflux.Store means you just have a class, not an instance of anything. Of course you can instantiate and use that store; however, if you just assign the class itself to this.store or this.stores in the Reflux.Component then it will automatically create a singleton instance of the store class (or use a previously created singleton instance of it if another component has already done so in its own construction). So, for example, to utilize the Reflux.Store store in the last example within a Reflux.Component class would look like this:

class Counter extends Reflux.Component
{
    constructor(props) {
        super(props);
        this.state = {};
        this.store = CounterStore; // <- just assign the class itself
    }
    
    render() {
        return <p>Count: {this.state.count}</p>;
    }
}

Note! Reflux.Store still works with instances of stores (i.e. the class must get intantiated). Assigning the class itself to this.store just allows Reflux to handle the instantiation and do some internal things that allow features like global state tracking. it does not mean that the class itself is the store. Internally Reflux creates and utilizes a singleton instance of the class. After mounting you may access that singleton instance of the class via MyStoreClass.singleton.

Utilizing Reflux.GlobalState

Another neat feature that the ES6 implementation of Reflux has is the ability to track a global state of all stores in use, as well as initialize all stores in use to a predefined global state. It happens internally too, so you don't have to do hardly anything to make it happen. This would be useful for many things, including tracking the state of an application and going back to that same state the next time the app is used.

To make it happen you just have to use ES6 style reflux classes and stores like explained in the last couple sections and define a static id property in your Reflux.Store definition. That id will then be used as a property name within the Reflux.GlobalState object for the property holding that store's current state. Then you just need to make sure to use setState to modify the state of the Reflux.Store instead of mutating the state directly. After that the Reflux.GlobalState object will reflect a collection of all your stores at all times once the components using those stores are mounted. An example using the example above:

class CounterStore extends Reflux.Store
{
	constructor() {
		this.listenables = Actions;
		this.state = {count:0};
	}
	
	onIncrement() {
		var cnt = this.state.count;
		this.setState({count:cnt+1});
	}
	
	static get id() {
		return 'counterstore';
	}
}

// ... make component and render as normal ...

console.log(Reflux.GlobalState); // <- would be: {'counterstore':{'count':0}}

Notice that you can only read the GlobalState after the components using the stores have been mounted. Up until then is the time where you can manually set the Reflux.GlobalState in order to initialize the entire app in a state of your choosing (or a previous state you recorded earlier). For example we could do this:

class CounterStore extends Reflux.Store
{
	constructor() {
		this.listenables = Actions;
		this.state = {count:0};
	}
	
	onIncrement() {
		var cnt = this.state.count;
		this.setState({count:cnt+1});
	}
	
	static get id() {
		return 'counterstore';
	}
}

Reflux.GlobalState = {'counterstore':{'count':50}};

// ... make component and render as normal ...

// at this point it would render with a count of 50!

One of the most useful ways you could do this is to store a Reflux.GlobalState state as a JSON string in order to implement it again the next time the app starts up and have the user begin right where they left off.

Making sure Reflux.Component is available

Reflux.Component extends React.Component. Therefore Reflux needs to be able to access React in order to expose it. If you need to load Reflux before React or if you are in an environment where React is not a global variable then there is an exposed method Reflux.defineReact that you can use to manually give Reflux a reference to the React object so that it may create the Reflux.Component class to extend from. A second optional argument also allows manually giving it a Reflux reference in case that isn't global either. An example on Node.js would be:

// only needed on some environments, usually Reflux just uses the globals!

// In Node.js, for example there won't be a global Reflux/React though...so:
var Reflux = require('reflux');
var React  = require('react');
Reflux.defineReact(React, Reflux);
// now Reflux.Component is accessible!

Back to top

Colophon

List of contributors is available on Github.

This project is licensed under BSD 3-Clause License. Copyright (c) 2014, Mikael Brassman.

For more information about the license for this particular project read the LICENSE.md file.

This project uses eventemitter3, is currently MIT licensed and has it's license information here.

refluxjs's People

Contributors

bripkens avatar bryangrezeszak avatar ccapndave avatar cmrnh 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 robcolburn avatar rymohr avatar shraymonks avatar simondegraeve avatar snorp avatar spoike avatar srph avatar turbo87 avatar undozen avatar vcarl avatar vslinko avatar willembult avatar

Watchers

 avatar  avatar

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.