Search code examples
reactjsreactjs-flux

ReactJs/Flux: How to handle editing process of store objects?


What's the best way to handle editing objects retrieved from the store?

E.g. lets say you have a ProductStore and you have a EditProductComponent. If you set the state product which you are editing like below:

this.state.product = ProductStore.getProductById(1); 

The problem is when the user cancels the edit after already making some changes, they persist on the store object as it is assigned to the state directly. E.g. user modifies the product name:

this.state.product.name = value; //new value is assigned to the store object as well

How can I make it performant and make it so that the store products stay read only?

I was thinking about returning a clone from the store using Object.assign to ensure that the original object stays untouched. But that would return new objects for each get request, which doesn't seem performant at all.


Solution

  • You'd have to be performing a lot of requests before creating a clone each time actually started to affect performance.

    Copying an object is not a particularly expensive operation and the garbage collector will clean up when your references go out of scope, so you probably won't need to worry about memory either.

    These are very small trade-offs compared to the benefit of the predictability that comes with knowing that the data in your stores can't be mutated randomly from elsewhere in your application.

    getProductById: function(id) {
      var product = this.products[id];
    
      return Object.assign({}, product);
    }
    

    However, you can go one level further and make them immutable instead.

    getProductById: function(id) {
      var product = this.products[id];
    
      return Object.freeze(product);
    }
    

    It's no longer possible to mutate the products now. If another part of your application wants to edit it, it must instead create a new copy with the appropriate changes applied.

    // the immutable product that came from the store
    const product = { ... };
    
    // with ES6
    const editedProduct = Object.assign({ editedProp: newValue }, product);
    // or with ES7
    const editedProduct = { editedProp: newValue, ...product };
    

    You'll also have to remember to update the store whenever you want to revise the object.

    ProductStore.edit(editedProduct.id, editedProduct);
    

    This allows you to enforce predictability across your application.

    The other alternative is to use a library which implements immutable data structures, such as Immutable.js.

    It comes with an immutable map type which can be used like an object, but instead of mutating it when you try to change or create a property, it returns a new immutable map.

    var product = Immutable.Map({ name: 'product_name' });
    var editedProduct = product.set('name', 'new_name');
    
    // now tell the store that you've edited it
    // ...
    

    The large benefit here is that Immutable.js uses a technique called structural sharing to make sure that internally as much of the object as possible is shared between the two versions, making them almost as efficient as regular mutable data structures.

    These concepts are covered well in a great video from the creator of the library. It's well worth a watch.