React+Reflux: Passing Variables to Data Stores

4k Views Asked by At

I'm building a React+Reflux application that, among other things, allows for the creation/deletion/editing of categories. So far I'm able to display all categories and handle creation/deletion of categories within a React component through associated stores and actions. This all works great, updates the database and re-renders the component as expected. My sticking point is when trying to drill down into a specific existing category in order to edit it.

I think I somehow need to pass a category Id to a store which will then pass it along to a php/sql query via an ajax call in order to get/set data specific to that particular category. If I bypass the store altogether and put the ajax call within the component itself I'm able to get it working via a url parameter with React-router (no auto re-render of course) but I haven't been able to figure out how to accomplish this through a store.

In other words, this more or less works:

  • "ManageCategories" React component that uses CategoryStore to list all categories each wrapped in an anchor tag that passes the category Id along to "ManageCategory" route/component
  • The "ManageCategory" component uses the category Id param directly in an ajax call within its getInitialState method to display data specific to the category

However, I think below is the more correct Reflux way to do this but I'm not sure how to get it to work:

  • "ManageCategories" component same as above
  • "ManageCategory" component that somehow passes its category Id param to the CategoryStore (or maybe a different "IndividualCategoryStore"?) which returns only data specific to that category and handles updates/edits to that category

I was able to get a sort of clunky version of this working by adding a new method ("getCategoryData") to the CategoryStore that is called in the getInitialState method of the "ManageCategory" component and is passed the categoryId param. This results in a flash of all categories (from the CategoryStore's getDefaultData) followed by the correct single category listing (from the component's getInitialState).

I feel fairly comfortable with the concepts behind React+Reflux but at this point I think it's likely I'm misunderstanding something fundamental. Worked on this particular issue for more than a week but none of the examples/tutorials/docs I've found address the specific question of passing a variable to a data store.

Actions:

var Actions = Reflux.createActions([
"createCategory",
"deleteCategory",
"editCategory"
]);

CategoryStore:

var CategoryStore = Reflux.createStore({
listenables: [Actions],
onCreateCategory: function(catName) {
    // ajax call to create new category that calls updateCategories on success
},
onDeleteCategory: function(catId) {
    // ajax call to delete category that calls updateCategories on success
},
updateCategories: function(){
    $.ajax({
        url: url + '?action=getAllCategories',
        async: false,
        dataType: 'json',
        success: function(categoryData) {
            this.categories = categoryData;
        }.bind(this),
        error: function(xhr, status, err) {
            console.error(url, status, err.toString());
        }.bind(this)
    });
    this.trigger(this.categories);
},
getDefaultData: function() {
    $.ajax({
        url: url + '?action=getAllCategories',
        async: false,
        dataType: 'json',
        success: function(categoryData) {
            this.categories = categoryData;
        }.bind(this),
        error: function(xhr, status, err) {
            console.error(url, status, err.toString());
        }.bind(this)
    });
    return this.categories;
}
});

Category Component:

var Category = React.createClass({
handleDeleteCategory: function() {
    Actions.deleteCategory(this.props.id);
},
render: function() {
    return (
        <li className="category">
            <IconButton icon="action-highlight-remove" onClick={this.handleDeleteCategory} />
            <h5><a href={"/#/manage-category/" + this.props.id}>{this.props.name} ({this.props.id})</a></h5>
        </li>
    );
}
});

ManageCategories Component:

var ManageCategories = React.createClass({
mixins: [
    Reflux.connect(CategoryStore, "categories")
],
getInitialState: function() {
    return {
        categories: []
    };
},
handleCreateCategory: function() {
    // category creation code
},
render: function() {
    var categoryNodes = this.state.categories.map(function(category) {
        return (
            <Category name={category.name} id={category.id} />
        )
    });
    return (
        <div className="dev-tools-container">
            <h1>Developer Tools</h1>
            <div className="categories">
                <h3>Categories</h3>
                <ul>
                    {categoryNodes}
                </ul>
                <h4>Create New Category:</h4>
                <form>
                    <label htmlFor="new-category-name">Category Name</label> <input type="text" id="new-category-name" /><br />
                    <PaperButton label="Create" primary={true} onClick={this.handleCreateCategory} />
                </form>
            </div>
        </div>
    );
}
});

Thanks in advance for any insights or assistance.

1

There are 1 best solutions below

4
On BEST ANSWER

After finally posting my question here I think I may have figured out where I was going astray all along. I was thinking in terms of passing the category Id to the store to filter the data therein when all I really need to do is take the full collection of data from the store and consume it selectively within the component.

So once routed to the ManageCategory component with the catId passed along as a url param, all I need to do is filter the data based on the catId.

For example, once in the ManageCategory component I can use lodash to filter and pluck the name value of the current category, as below. No need to edit the collection of data stored in the CategoryStore at all.

var ManageCategory = React.createClass({
mixins: [
    Reflux.connect(CategoryStore, "categoryData")
],
getInitialState: function() {
    return {
        categoryData: []
    };
},
render: function() {
    var categoryName = _.chain(this.state.categoryData)
                        .filter({"id": this.props.params.catid})
                        .pluck("name");

    return (
        <div className="category-container">
            <h1>{categoryName}</h1>
        </div>
    );
}
});

Feel free to let me know if there's a better way of doing this but for now this is exactly what I needed. Hopefully something from all of this will be helpful to someone else too.