FLUX

Une architecture pour le JavaScript front-end

ChtiJS #13 - 2/07/2015

MOI (UN PEU QUAND MÊME)

  • Tom Panier aka neemzy
  • Ingénieur SI chez CGI
  • Dev PHP et expert technique pour Auchan E-Commerce France
  • Fait aussi du JS, dont entre autres du React

C'EST L'HISTOOOIRE
 D'UNE APPLIII

LE CONTEXTE

  • Appli métier back-office
  • UI fabriquée avec React
  • Beaucoup d'avantages
  • Des utilisateurs comblés
  • Des devs aux anges

MAIS UN BEAU MATIN...

C'EST LE BORDAYL

Problème

  • Beaucoup de composants React...
  • ...qui font des appels AJAX, implémentent de la logique...
  • ...bref, qui font le café.

SOLUTIONS

  • Passer sur Angular
  • Restructurer l'appli
</troll>

Flux

  • Ni librairie, ni framework : un modèle d'architecture
  • Conçu par Facebook
  • Basé sur (mais découplé de) React
  • Plus adapté au front-end que le MVC

EXEMPLE > EXPLICATION

var ItemApp = (function () {
    return React.createClass({
        getInitialState: function() {
            return { items: [] };
        },

        fetchItems: function() {
            taLibAjaxFavorite
                .get('/items')
                .then(function (items) {
                    this.setState({ items: items });
                });
        },

        createItem: function(data) {
            taLibAjaxFavorite
                .post('/items', data)
                .then(function (items) {
                    this.setState({ items: items });
                });
        }

        componentWillMount: function() {
            this.fetchItems();
        },

        render: function() {
            return (
                <div>
                    <h1>Super useful item list</h1>
                    <ItemList items={this.state.items} />
                    <ItemForm createItem={this.createItem} />
                </div>
            );
        }
    });
})();

POURKOI CÉ MAL ?

  • La vue communique directement avec le serveur
  • React gère les données dans les deux sens
var ItemApp = (function () {
    var getAppState = function() {
        return { items: ItemStore.getItems() }; // Store
    };

    return React.createClass({
        getInitialState: function() {
            return getAppState();
        },

        onChange: function() {
            this.setState(getAppState());
        },

        componentWillMount: function() {
            ItemActions.fetchItems(); // Action
        },

        componentDidMount: function() {
            ItemStore.addChangeListener(this.onChange); // Store
        },

        componentWillUnmount: function() {
            ItemStore.removeChangeListener(this.onChange); // Store
        },

        render: function() {
            return (
                <div>
                    <h1>Super useful item list</h1>
                    <ItemList items={this.state.items} />
                    <ItemForm createItem={ItemActions.createItem /* Action */} />
                </div>
            );
        }
    });
})();

SURVOL

  • Actions
  • Dispatcher
  • Stores

ACTIONS

var ItemActions = (function () {
    return {
        fetchItems: function() {
            Dispatcher.handleAction({
                actionType: Constants.ITEM_FETCH
            });
        },

        createItem: function(data) {
            Dispatcher.handleAction({
                actionType: Constants.ITEM_CREATE,
                data: data
            });
        }
    };
})();

DISPATCHER

var Dispatcher = (function () {
    var dispatcher = new Flux.Dispatcher(); // dépendance 1

    dispatcher.handleAction = function(action) {
        this.dispatch({
            action: action
        });
    };

    return dispatcher;
})();

STORES

var ItemStore = (function () {
    var items = [],

        loadItems = function(data) {
            items = data;
        },

        store = {};

    Object.assign(
        store,
        EventEmitter.prototype, // dépendance 2
        {
            getItems: function() {
                return items;
            },

            emitChange: function() {
                this.emit('change');
            },

            addChangeListener: function(callback) {
                this.addListener('change', callback);
            },

            removeChangeListener: function(callback) {
                this.removeListener('change', callback);
            }
        }
    );

    // ...
    // ...

    Dispatcher.register(function (payload) {
        switch (payload.action.actionType) {
            case Constants.ITEM_FETCH:
                taLibAjaxFavorite
                    .get('/items')
                    .then(function (data) {
                        loadItems(data);
                        store.emitChange();
                    });

                break;

            case Constants.ITEM_CREATE:
                taLibAjaxFavorite
                    .post('/items', { value: payload.action.data })
                    .then(function (data) {
                        loadItems(data);
                        store.emitChange();
                    });

                break;
        }

        return true;
    });

    return store;
})();

LE RÉSULTAT ?

  • Code plus clair, lisible, maintenable...
  • Des devs (de nouveau) aux anges

MERCI !

Flux : une architecture pour le JavaScript front-end

By Tom Panier

Flux : une architecture pour le JavaScript front-end

ChtiJS #13 - 2/07/2015

  • 4,624