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

Related tags

Data Store refluxjs
Overview

RefluxJS

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

NPM Version Bower Version Build Status NPM Downloads

Sauce Test Status


Installation

You can currently install the package as a npm package, a bower component, or import it from a CDN.

NPM

The following command installs RefluxJS as a npm package:

npm install reflux

Then, in your script, you can gain a reference to RefluxJS like so: var Reflux = require('reflux');

Bower

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

bower install reflux

Then the files may be imported into your html file via bower_components/reflux/dist/reflux.js or bower_components/reflux/dist/reflux.min.js. At that point a Reflux variable will be globally available to you. It is suggested that you import RefluxJS after React.

CDN

RefluxJS is available at jsdelivr.

You may import the CDN files directly through a script tag. At that point a Reflux variable will be globally available to you. It is suggested that you import RefluxJS after React.


Overview

The main function of Reflux 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.


Usage

For usage, you need to create actions which can be called from React components. Those actions are listened to by stores which hold and update data. In turn those stores are hooked up to React components and set state within them as it is updated within the store.

Therefore the 3 main concepts to know are:

  1. creating actions
  2. creating stores
  3. hooking stores to React components

Creating Actions

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

var statusUpdate = Reflux.createAction();

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

statusUpdate(data); // Invokes the action statusUpdate

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();

More on Actions:

Actions can also:

  • load files asynchronously with child actions
  • do preEmit and shouldEmit checking
  • have many shortcuts for easy usage

See Reflux Action Documentation for more.


Creating Stores

Create a data store much like ReactJS's own React.Component by creating a class extending Reflux.Store. The store has a state property much like a component, and uses setState like a component as well. You may set up all action listeners in the constructor and register them by calling the store's own listenTo function.

class StatusStore extends Reflux.Store
{
    constructor()
    {
        super();
        this.state = {flag:'OFFLINE'}; // <- set store's default state much like in React
        this.listenTo(statusUpdate, this.onStatusUpdate); // listen to the statusUpdate action
    }

    onStatusUpdate(status)
    {
        var newFlag = status ? 'ONLINE' : 'OFFLINE';
        this.setState({flag:newFlag});
    }
}

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

Stores also integrate easily with sets of actions via things like this.listenables. When an actions object (or an Array of multiple actions objects) is applied to this.listenables you may automatically add listeners simply by naming convention. Just name the functions either after the action name (such as actionName, or the camelcased action name preceded with "on", (such as onActionName).

var Actions = Reflux.createActions(['firstAction', 'secondAction']);

class StatusStore extends Reflux.Store
{
    constructor()
    {
        super();
        this.listenables = Actions;
    }

    onFirstAction()
    {
        // calls on Actions.firstAction();
    }

	onSecondAction()
	{
		// calls on Actions.secondAction();
	}
}

More on Stores:

Reflux stores are very powerful. They can even do things like contribute to a global state that can be read and set for partial or full-state time-travel, debugging, etc.

See Reflux Store Documentation to learn more about stores.


Hooking Stores to Components

Once you've created actions and stores, now the last step in working RefluxJS is to hook those stores to a React component.

This is done as simply as extending Reflux.Component instead of React.Component and setting the store(s) to use. Reflux.Component itself extends React.Component, so you use them the exact same way. The only difference is that Reflux.Component allows you to set stores for the component to get state from:

class MyComponent extends Reflux.Component
{
    constructor(props)
    {
        super(props);
        this.state = {}; // our store will add its own state to the component's
        this.store = StatusStore; // <- just assign the store class itself
    }

    render()
    {
        var flag = this.state.flag; // <- flag is mixed in from the StatusStore
        return <div>User is {flag}</div>
    }
}

When the component mounts it will either create a singleton instance of StatusStore (if one isn't already made) or use an already made singleton (if it was already created by another component that uses the store).

Of important note is that you can:

  1. Set multiple stores by setting this.stores (the plural) and setting it to an Array of store classes.
  2. Set a this.storeKeys Array to restrict only certain parts of the store being mixed into the component state.

There is also a mapStoreToState method in the documentation for those that want absolute control over how a store's state is mapped to a component.

class MyComponent extends Reflux.Component
{
    constructor(props)
    {
        super(props);
        this.state = {type:'admin'}; // <- note that we can still use normal state
        this.stores = [StatusStore, AnotherStore];
        this.storeKeys = ['flag', 'info'];
    }

    render()
    {
        var flag = this.state.flag;
        var info = this.state.info;
        var type = this.state.type;
        return <div>User is {flag}, info: {info}, type: {type}</div>
    }
}

The above will mix in properties from the state of both StatusStore and AnotherStore. However, because of this.storeKeys it will only mix in the properties flag and info from them. So any other properties within those stores will not get mixed in. So even if a store contained a type property in its state it would not get mixed in, and the type value we set as a normal part of the component state is safe.

More on using Reflux style components:

Reflux's simple and intuitive way of integrating stores into components is easy and powerful. You can aggregate stores together on a component-by-component basis, filter which parts of the stores come through and which don't, or even do a detailed manual mapping of exactly how you want the state from stores to map to the state in a particular component.

See Reflux Style Component Documentation to learn more.


Documentation

What you've just read is a "view from 10,000 feet" type overview of getting started with RefluxJS. For serious learning see the documentation.

Comments
  • Make reflux isomorphic friendly

    Make reflux isomorphic friendly

    Feedback I receive around suggest that reflux needs to be more friendly for isomorphic projects, i.e. same actions and stores can be run both client-side and server-side.

    The goal with this issue is how can we achieve this in reflux and still keep a simple API?

    Suggestions and PR's are welcome.

    enhancement help wanted resource 
    opened by spoike 94
  • Textarea's cursor jumps when changing its value through reflux

    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, https://github.com/facebook/react/issues/1698

    question 
    opened by raine 32
  • API Additions

    API Additions

    Added a few API elements to better make use of what's going on with Reflux.GlobalState

    Main two additions:

    Reflux.getGlobalState and Reflux.setGlobalState methods. Former allows a deep clone of the Reflux.GlobalState which won't continue to mutate (so you can easily store snapshots of the global state) and the latter allows you to set the global state at any point (including with partial global state objects that will only override the stores/properties represented). This basically allows an API for state based time travel.

    Also added is the Reflux.initializeGlobalStore method. With expanded global state ability comes more usefulness in working with ES6 stores both in and outside of components. This allows instantiation of a proper singleton instance of a store and setting it up for tracking by the global state without any need to attach to a component.

    Tests, documentation, and a lot of code commenting was also added.

    opened by BryanGrezeszak 30
  • adding the listenables prop to createStore

    adding the listenables prop to createStore

    This adds a convenient listenables prop to createStore, which lets you easily listen to all listenables in an object (read: the return value from createActions).

    Here's the example added to the README. If you've previously done this:

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

    ...then instead of doing this:

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

    ...you can now do this:

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

    To enable this to be used with an object of stores too (hence the agnostic name listenables), I added support for getDefaultData in the listenable. That data is sent to the listening callback, or a this.<callbackname>Default if that exists.

    enhancement 
    opened by krawaller 28
  • Triggering discrete actions (via namespace or something similar) from a store?

    Triggering discrete actions (via namespace or something similar) from a store?

    It's entirely possible that this is an architecture question, and not a functionality question. I'm coming from a backbone background, where we can namespace events coming out of a model: this.trigger('namespace:eventName', args);

    That namespace allows me to have various parts of an app share a model, but only take an action if they are listening for that particular eventName and namespace. Is there something like this in Reflux, or am I missing something in the inherent architecture here? All of the examples have a store firing a trigger event, with the entirety of its data.

    For example, if I have an AppStore, and it hold the states for a few things (modal visibility, which menu is open). My modal component only needs to care about whether there's some modal data, my menus only which menu is active, etc. Thanks.

    question 
    opened by kwhitaker 27
  • Not working with browserify

    Not working with browserify

    While browserify compiles this library fine, when I try to use it, it get this in the console:

    Uncaught Error: Cannot find module '../src'

    and when I look at the browserify generated code, I see stuff like this:

    },{"../src":undefined,"./utils":174}],169:[function(require,module,exports){
    

    Why is this not working (using version 0.1.15)?

    bug 
    opened by ryanzec 27
  • Stores and Actions cannot be removed from memory

    Stores and Actions cannot be removed from memory

    I was profiling our application and it seems like there's a potential memory leak in Reflux - the Keep object holds references to all the created Stores and Actions (createdStores, createdActions). I don't think there's any official way to remove these references, which causes a leak if your stores aren't singletons.

    I know that as per the official Flux rules, stores should be singletons, but this rule is not exactly written in stone. Would it be possible to provide official API to release stores and actions completely? If not, I think it should at least be mentioned in the docs.

    opened by vladimir-rovensky 26
  • ES6 class support

    ES6 class support

    Edit by @BryanGrezeszak: I'm putting this at the top since this page gets a lot of google traffic. Reflux does now support and embrace ES6 classes in React. You may check the documentation for more info.


    It would have been fantastic if I could do:

    class MyStore extends Reflux.Store { init(){ ... } }

    This would allow editors/jshint/flow to more easily do code checks, like checking method names, than what is possible with a dynamic object that Reflux.createClass gives.

    Any chance for this in the near future? :)

    enhancement help wanted 
    opened by torarnek 26
  • Child (async) actions and promise handling

    Child (async) actions and promise handling

    Async actions

    There has been some discussion on asynchronous actions (https://github.com/spoike/refluxjs/issues/57, https://github.com/spoike/refluxjs/pull/103) and where to best handle them.

    The consensus seems to be that triggered execution is best kept outside of the stores, and I personally agree. In my opinion, the store shouldn't worry about how actions are run, focusing merely on their results. It also makes testing easier, as a few folks have noted.

    The pattern proposed in the update on v0.1.8 links the execution of asynchronous code to the preEmit hook of an action, after which it triggers events on "Completed" and "Failed" actions, like so:

    var ProductAPI = require('./ProductAPI');  
    // contains `load` method that returns a promise
    
    var ProductActions = Reflux.createActions({  
        'load',         // initiates the async load
        'loadComplete', // when the load is complete
        'loadError'     // when the load has failed
    });
    
    // Hook up the API call in the load action's preEmit
    ProductActions.load.preEmit = function () {  
         // Perform the API call, get a promise
         ProductAPI.load()
              .then(ProductActions.loadComplete)
              .catch(ProductActions.loadError);
    };
    

    Issues with preEmit

    While this is definitely the cleanest way I've seen, some things still bothered me about it:

    • There is no semantic link between the "load" and "loadComplete" / "loadError" events. Nothing in the interface reflects the fact that they are related.
    • Putting an API call, or some other asynchronous code, in the preEmit hook seems hacky to me. Others have expressed similar sentiments (https://github.com/spoike/refluxjs/issues/57).
    • I think it's overly verbose. There's still some unnecessary boilerplate here, and for every async event (of which there can be loads) we have to now manually define 3 separate actions. If at some point we want to add a "Canceled" action as well for all our calls, we'll be doing busy-work for a while.

    Proposal: Child actions

    It appeared to me that if we could define child actions (attached to a parent action), we could design a much nicer interface. This commit is an implementation of that idea. The general interface looks as such:

    // ProductActions.js
    
    var ProductAPI = require('./ProductAPI');  
    // contains `load` method that returns a promise
    
    // Define action with child actions
    var ProductActions = Reflux.createActions({  
        'load': {children: ['completed','failed']}
    });
    
    // And here is how you would hook it up to a promise
    ProductActions.load.listen( function() {
        // Bind listener context to action by default
        ProductAPI.load()
            .then(this.completed)
            .catch(this.failed);
    });
    
    // ProductStore.js
    
    var ProductActions = require('./ProductActions');
    
    // This is how you would listen for a completed load
    ProductActions.load.completed.listen(function(result) {
        // do something with result
    });
    
    // listenToMany (and ListenerMixin) will still work intuitively
    var Store = Reflux.createStore({
        init: function() {
            this.listenToMany(ProductActions);
        },
        onLoad: function(){
            // product load started
        },
        onLoadCompleted: function(product){
            // product load completed
        }
    });
    

    Common-case helpers

    I figured it would make sense as well to provide some helper methods and options to make the 80% case easier. This works as such:

    // Define actions with async option to auto-create
    // 'completed' and 'failed' child events
    var ProductActions = Reflux.createActions({  
        'load': {async: true}
    });
    
    // Hook up standard async child functors
    // to a promise easily:
    ProductActions.load.listen( function() {
        // Have the action attach callbacks to the promise
        // and pass on data to "complete" and "failed" actions
        ProductActions.load.promise(ProductAPI.load());
    });
    
    // Or even easier for the 80% case, a method that
    // hooks ups callbacks to a returned promise directly
    ProductActions.load.listenAndPromise( function() {
        return ProductAPI.load();
    });
    

    Alternative uses

    For custom cases, you could create alternative children events:

    var ImageAPI = require('./ImageAPI');
    
    // If we need custom child actions, we could do so:
    var ImageActions = Reflux.createActions({
        'pictureUpload': {children: ['progressed','completed']}
    });
    
    // Hook things up to a non-promise-based API
    ImageActions.pictureUpload.listen(function(){
        ImageAPI.upload({
            onProgress: this.progressed,
            onSuccess: this.completed
        });
    });
    

    Notes

    Note, while createActions now operates on an associative Object, the interface remains backwards-compatible and accepts Arrays as well.

    This has cleaned up my action definitions quite a bit. Curious to hear your critical thoughts / feedback! Thanks!

    enhancement 
    opened by willembult 26
  • Disambiguating data triggers.

    Disambiguating data triggers.

    I have an async action to fetch a Thing from API.

    I also have an async action to POST a new Thing to the API.

    The natural payload for the corresponding triggers in the ThingStore is the Thing.

    The problem arises when a component asks for a Thing, and while waiting for the async stuff, another component fires the action to POST a Thing.

    Now at some point we're going to have two triggers fire, each with a Thing, and no way to know which is the result of which action. How is the component which POST'ed a Thing to know which is which? After all, it doesn't know how many Things to expect, maybe the store will actually be sending a fail trigger.

    question 
    opened by dmwyatt 25
  • Asynchronous dataflow example

    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.

    question 
    opened by simenbrekken 23
  • Please mark project as dead

    Please mark project as dead

    It might be a good idea to either archive the repository, or add a note in README.md that the project is in fact dead. It would be very much apprieciated if you'd give some migration propositions too. I've seen https://github.com/mobxjs/mobx mentioned in few of opened issues - is this the recommended library to switch to?

    opened by magicznyleszek 0
  • Feature Request: Make Reflux available for Functional Components

    Feature Request: Make Reflux available for Functional Components

    I really like the idea of Reflux and would like to use it. Unfortunately, our project only uses functional components. From the documentation I didn't see a way to use Reflux with functional components. If there is a way, please let me know. Thanks! Chris

    opened by dutchman71 1
  • Cannot Use Reflux Components With React getDerivedStateFromProps() Static Method

    Cannot Use Reflux Components With React getDerivedStateFromProps() Static Method

    React 16.4.1 Reflux 6.4.1


    On components that extend Reflux.Component, if I use

    static getDerivedStateFromProps(props, state) {...}
    

    without implementing componentWillMount() (which is now UNSAFE in React), the Reflux functionality in the component fails, and I get a console error:

    image

    In the screenshot, below the error, I printed this.componentWillMount from the component's render() method, attesting it comes from the Reflux.Component implementation.

    In summary, due to implementing a now UNSAFE componentWillMount(), Reflux.Component cannot be used with the new static method getDerivedStateFromProps().

    opened by tgv1975 3
  • async actions return undefined instead of Promise

    async actions return undefined instead of Promise

    I have an action defined as

    doSomething: { asyncResult: true}

    in the code I have

    doSomething("some params").then(..).catch(...)

    This used to work in 0.2.x versions. After upgrade to 5.0.4 action invocation started to return undefined which naturally was throwing a JS exception.

    Why is this no longer working? What is an alternative approach if this is not a bug?

    opened by andpor 3
Owner
null
Isomorphic flux implementation

alt Check out the API Reference for full in-depth docs. For a high-level walk-through on flux, take a look at the Getting Started guide. What follows

Josh Perez 3.4k Dec 21, 2022
A pluggable container for universal flux applications.

Fluxible This repository is the home of Fluxible and related libraries. For support, join our gitter.im channel. Development Development is currently

Yahoo 1.8k Dec 14, 2022
A performant, scalable and pluggable approach to instrumenting your React application.

react-i13n react-i13n provides a performant, scalable and pluggable approach to instrumenting your React application. Typically, you have to manually

Yahoo 369 Dec 25, 2022
Selector library for Redux

Reselect Simple “selector” library for Redux (and others) inspired by getters in NuclearJS, subscriptions in re-frame and this proposal from speedskat

Redux 18.8k Dec 27, 2022
Dead simple + opinionated toolkit for building redux/react applications

Simple opinionated toolkit for building applications using React, Redux, and Immutable.js You're early! There is sparse documentation, no tests, and i

shasta (archive) 517 Sep 29, 2022
React integration for Baobab.

baobab-react Welcome to baobab's React integration (from v2.0.0 and onwards). Implemented patterns: Hooks Higher order components (curried so also usa

Guillaume Plique 309 Sep 29, 2022
[UNMAINTAINED] Reactive state and side effect management for React using a single stream of actions

Flexible state and side effect manager using RxJS for React. About Fluorine provides you with easy, reactive state and side effect management, accumul

Phil Pluckthun 285 Nov 20, 2022
React bindings for MobX

mobx-react ?? ?? ?? This repo has been moved to mobx Package with React component wrapper for combining React with MobX. Exports the observer decorato

MobX 4.9k Dec 27, 2022
Official React bindings for Redux

React Redux Official React bindings for Redux. Performant and flexible. Installation Using Create React App The recommended way to start new apps with

Redux 22.5k Jan 3, 2023
Declarative data-fetching and caching framework for REST APIs with React

resourcerer resourcerer is a library for declaratively fetching and caching your application's data. Its powerful useResources React hook or withResou

Noah Grant 75 Jun 24, 2022
keepalive-react-component是一个react缓存组件

基于react开发出来的react缓存组件,类似于类似vue的keepalive包裹vue-router的效果功能

null 25 Dec 29, 2022
how to use react ddd with TODO

分析你的应用有哪些功能,功能之间有哪些关系 要花费 2-3 倍的时间 人没有办法做出一个自己都不了解的东西,即便你用 分层架构 即刻开始写,这些问题也只是延迟出现,一旦出现,反而会更加难以调试,甚至完全不可用 useXxxService 是自定义服务,useXxx 一般是第三方,比如 useRequ

Prajna 16 May 5, 2022
COVID-19 Data: built with react and redux by using a public covid api

COVID-19 Data This is a React module capstone project. It is built with react and redux by using a public covid api. Screenshot Live Demo Live Link Vi

Tarikwa Tesfa 11 Jun 3, 2022
A simple library for uni-directional dataflow application architecture with React extensions inspired by Flux

RefluxJS A simple library for unidirectional dataflow architecture inspired by ReactJS Flux. Installation You can currently install the package as a n

null 5.4k Dec 21, 2022
Boilerplate for react universal (isomorphic) application based on flux architecture (redux implementation)

Redux universal boilerplate Boilerplate for react universal application building on flux architecture based on redux implementation. Boilerplate based

Sergey 73 Mar 30, 2022
:hammer_and_wrench: Flux architecture tools for React

Fluxxor is a set of tools to aid in developing React applications with the Flux architecture. Installation Fluxxor is available on npm and works with

Michelle Tilley 1.7k Nov 3, 2022
A generic drag-and-drop dataflow editor for React

react-dataflow-editor A generic drag-and-drop dataflow editor for React. ✨ You can read about the design of this component in this blog post! Table of

Joel Gustafson 137 Jan 2, 2023
Cristian Alejandro 12 Dec 15, 2022
Stable bi-directional infinite scroll React component

Stable bi-directional infinite scroll React component. Load additional data from both ends of a container while maintaining current view. Used for chat, timeline, feed views.

Cathy Chen 15 Oct 20, 2022
A mobile support and multi-directional modal for ReactJS

React Poppop A responsive, mobile support, multi directions and easy to use modal for ReactJS. Compatible with React 15 and 16. Demo Features Mobile s

Chen-Tai Hou 80 Sep 26, 2022