Flux explained for newbies - Part 3

Reading Time: 4 minutes

Introduction

Flux-CapacitorIn the second part of this article series I explained that Flux is an architectural pattern and introduced the concept of a Store, one of the essential elements of Flux. I gradually explained the meaning and functionality of a Store and how it differs from the concept of a classical model. I emphasized that a Store in Flux is responsible for maintaining and updating its application state, and notifying interested components. I concluded the article with code snippet demonstrating the usage of a Store using a fictitious Flux implementation. This part introduces the Dispatcher.

The Dispatcher

Just to remember, we have constructed an architecture with several components updating and listening multiple Stores. Each Store has its own interface. This is already a quite flexible and powerful concept (a typical Publish-Subscribe pattern). To facilitate the access to the Stores we now introduce a helper element that centralizes all Store interfaces in one point. This is called the Dispatcher.

Dispatcher centralizes access to Stores

 

The Dispatcher delegates/propagates the update actions to the related Store. The original Flux pattern by Facebook defines that there is only one global Dispatcher (Singleton) in one application. I personally don't mind if an application has more than one Dispatcher. Although I never encountered a situation yet, where more than one Dispatcher was necessary, I can imagine that several Dispatcher instances can be used for logical aggregation purposes.

The following code is an adapted version from the prior part of this article series. Here, we continue with our fictitious FooFlux library introducing the Dispatcher. The components still listen to the stores, but for updating the different application states we use the dispatch() method. As the dispatcher is a singleton, our library always returns the one and only instance via getDispatcher(). In this scenario it is essential to tell the Dispatcher what action triggers which method of which store. In the code it is demonstrated using the Dispatchers register() method.

var personStore = FooFlux.createStore({
    id : 'personStore',
    _persons : [],
    add : function(person){
        this._persons.push(person);
        this.notify(this._persons);
    }
});

var productStore = FooFlux.createStore({
    id : 'productStore',
    _products : [],
    add : function(product){
        this._products.push(product);
        this.notify(this._products);
    }
});

// Establish relations between dispatchers action names and the stores target methods
FooFlux.getDispatcher().register(function(actionName, data) {
    switch (actionName) {
        case 'addProduct':
            productStore.add(data);
            break;
        case 'addPerson':
            personStore.add(data);
            break;
    }
});

var productEditor = FooComponentLib.createComponent({

    product : {}, // internally updated via common data binding

    onAddButtonClick : function(){
        // propagate our intent using the singleton dispatcher
        FooFlux.getDispatcher().dispatch('addProduct', this.product);
    },

    render : function(){
       // .. code for rendering
    }

});

var personEditor = FooComponentLib.createComponent({

   person : {}, // internally updated via common data binding

   onAddPersonButtonClick : function(){
       // propagate our intent
       FooFlux.getDispatcher().dispatch('addPerson', this.person);
    },

    render : function(){
        // .. code for rendering
    }

});

var personList = FooComponentLib.createComponent({

     // called when store was updated
    onChanged : function(persons){
       this._persons = persons;
       this.render();
    },

    componentReady : function(){
       // listen to store
       FooFlux.getStore('personStore').listen(this.onChanged.bind(this));
    },

    render : function(){
       // .. code for rendering
    }

});

Update Dependencies

In certain situations it may be necessary that one update depends on another update. So, this is why many Flux implementations provides a Dispatcher with a waitFor() method. This way the execution of an Action is deferred until a dependent Action was executed. The following example is merely constructed, and should only demonstrate the purpose of waitFor(). In my opinion, Actions and/or Stores should not depend on each other, as it indicates a data design flaw. Creating dependency chains can really mess up the code.

var selectPersonAction = FooFlux.getDispatcher().register(function(actionName, person) {
    if (actionName === 'selectPerson') {
            personStore.select(person);          
    }
});

FooFlux.getDispatcher().register(function(actionName, product) {
     if( actionName === 'addProductToCart'){
        // waitFor() is blocking, like in Facebooks Dispatcher
        this.waitFor(selectPersonAction); 
        // will be executed when selectPersonAction was executed
        cartStore.add(personStore.getSelected(), product);
    }
});

As you can see, the Dispatcher is nothing really complicated. In the last part of this series I will present the missing piece of a full Flux architecture- The Action Creator.

Facebooktwittergoogle_plusredditpinterestlinkedin

Leave a Reply

Your email address will not be published. Required fields are marked *