Search code examples
javascriptsortingreactjsreactjs-flux

React: Sort function causing retroactive side effects my store?


Tools:

Reactjs 0.14.0

Vanilla Flux(not sure if that affects the nature of this question.


I ran into a bug today that had me scratching my head for awhile. In my store I was trying to access the first element in an array but it wasn't the expected element.

I did print outs of the array as it entered the store and even after it emitted that the store was updated and the array looked as it should but if I accessed it eg data[0] it wasn't was the print out showed.

Afterwards I finally found that a sort was being called AFTER the data was sent to the store.

Like So:

ServerActionCreators.receiveData(data);
data.sort(mySortingFunction);

My Question:

Why is the data in the store being retroactively updated? How do React developers ensure that these retroactive side effects don't affect the synchronous manipulation of their stores?


Solution

  • The problem occurs because both you and the store hold references to the same array and the sort method mutates it.

    It is possible to avoid this behaviour by staying well away from methods that directly mutate arrays (push, pop, shift, unshift, reverse and sort) or making a copy of the array before performing any of these actions.

    var data = [ ... ];
    
    // give the store a reference
    MyStore.sendData(data);
    
    // copy the reference before mutating it
    var sorted = copy(data).sort(sortFn);
    
    // continue to work with the copy
    // ...
    

    There are a number of ways to copy an object or a list, depending on which version of Javascript you are using.

    // ES7
    const copy = list => [...list]
    // ES6
    const copy = list => Object.assign([], list);
    // ES5
    function copy(list) {
      return list.map(function(item) {
        return item;
      });
    }
    

    Alternatively, you could use an immutable list or vector to solve this problem.

    // mori.js
    var data = mori.vector( ... ),
        sorted = mori.sort(sortFn, data);
    
    // immutable.js
    var data = Immutable.list( ... ),
        sorted = data.sort(sortFn);
    

    By definition these data structures won't be mutated when you call their methods.