1

I've managed to create a cycle in my bacon-powered application, so I need some advice on how to break it.

Let's say we have several properties which carry some state of the application. This state is supposed to be stored via HTML5 History API (pushState) and thus I combine those properties to an object and execute a pushState on it. However, I also need to cover popstate events, so on every popstate (back button, etc.) I split the state object back into single properties and inject them into their original streams.

Now I've got myself a cycle: since those property streams are the same streams I got my pushState from in the first place, pushState is called again, duplicating states on the stack and making the back button useless.

This is of course my mistake, however, I don't see a good solution apart from splitting each property stream into two and combining them differently in pushState/popState-specific cases. But that seems rather unelegant -- is there any canonical way to avoid this kind of cycles?

Nikolai Prokoschenko
  • 8,465
  • 11
  • 58
  • 97

1 Answers1

1

The most elegant solution I come up is to check the current state before pushing new one. If they are the same, do nothing:

// state properties
var foo = ...;
var bar = ...;

// write
var history = Bacon.combineAll(makeHistoryState, foo, bar);

history.onValue(function (v) {
    if (!_.isEqual(history.state, v)) {
        history.pushState(v);
    }
});

// read
window.addEventListener("popstate", function(event.state) {
    foo.set(extractFoo(event.state));
    bar.set(extractBar(event.state));
});

Disclaimer: I didn't test this myself

EDIT: when having global state as single property, which is saved to history API as is; everything simplfies. And you can probably leverage Bacon.Model lens functionality

phadej
  • 11,947
  • 41
  • 78