Search code examples
javascriptreactjsrefluxjsdatastore

Reflux: having multiple components access the same Store


tl;dr: The UUID for the GraphStore changes every time I add a new Graph to it. This leads me to assume each Graph is creating its own unique GraphStore. I want them all to share a single store.

I have a React Dashboard component that contains multiple Graph components.

My Graph component is passed an id props from the Dashboard. Using that id, it then looks for data in a graphs array stored in a GraphStore. However, it seems to me that each Graph is creating its own GraphStore, rather than all sharing the same (the desired behavior). How do I make them all use the same GraphStore?

I thought about passing in the correct GraphStore from the dashboard, but then it is not possible for me to have each Graph listen for changes from the GraphStore.

I'm happy to not use Reflux.connectFilter, but it seems like the perfect thing for this.

My code (the key parts at least):

Dashboard

var React        = require('react');
var Graph        = require('./graph').Graph;
var GraphActions = require('./graphActions').GraphActions;
var UUID         = require('uuid');

var Dashboard = React.createClass({
    ...
    render: function() {
        var graphs = [];
        for(var i = 0; i < 10; ++i) {
            var id = UUID.v4();
            GraphActions.createGraph(id);
            graphs.push(
                <Graph id={id} />
            );
        }
    }
});

module.exports = {Dashboard: Dashboard}; 

Graph

var React      = require('react');
var GraphStore = require('./graphStore').GraphStore;

var Graph = React.createClass({
    mixins: [Reflux.connectFilter(GraphStore, "graph", function(){
        return graphs.filter(function(graph) {
            return graph.id === this.props.id;
        }.bind(this))[0];
    })],
    propTypes: {
        id: React.PropTypes.string
    },
    render: function() {
        // Needed because the AJAX call in GraphStore might not have completed yet
        if(typeof this.state.graph == "undefined") {
            return (<div>Graph loading...</div>);
        }

        return (<div>Data: {this.state.graph.data}</div>);
    }
});

module.exports = {Graph: Graph};

GraphStore

var Reflux       = require('reflux');
var jQuery       = require('jquery');
var GraphActions = require('./graphActions').GraphActions;
var UUID         = require('uuid');

var GraphStore = Reflux.createStore({
    listenables: [GraphActions],
    onCreateGraph: function(graphId) {
        console.log("GraphStore " + this.id + " is adding new graph " + graphId);

         jQuery.ajax({
              ...
              success: this.addGraph
         });
    },
    addGraph: function(data) {
        this.graphs.push(
            {
                id:   graphId,
                data: data
            }
        );

        this.trigger({graphs: this.graphs});
    },
    getInitialState: function() {
        this.graphs = [];

        // Here I give the store a UUID so I can identify it later
        this.id = UUID.v4();

        return {
            graphs: this.graphs
        };
    }
});

Solution

  • getInitialState on the Reflux Store fires each time a component subscribes to the store (it's the initial data for the component).

    If you require something to only run once on the store, use init:

    var GraphStore = Reflux.createStore({
        listenables: [GraphActions],
        init: function() {
            this.graphs = [];
    
            // Here I give the store a UUID so I can identify it later
            this.id = UUID.v4();
        },
        getInitialState: function() {
            return {
                graphs: this.graphs
            };
        }
    });