I’m trying to synchronize browser history with state contained within an onionify state store in a very simple cycle js example application. I have no problem mapping from state store to history, the issue is in reducing history.state back into the state store. Essentially, I get caught in an infinity loop of state streams flowing into one another, this crashes the browser.
import { run } from "@cycle/run";
import { div, button, p, makeDOMDriver } from "@cycle/dom";
import onionify from "cycle-onionify";
import { makeHistoryDriver } from "@cycle/history";
import xs from "xstream";
const main = (sources) => {
const popHistory$ = sources.history; // This is the problem...
const action$ = xs.merge(
sources.DOM.select(".decrement").events("click").map( () => -1 ),
sources.DOM.select(".increment").events("click").map( () => +1 ),
);
const state$ = sources.onion.state$;
const vdom$ = state$.map( state =>
div([
button(".decrement", "Decrement"),
button(".increment", "Increment"),
p(`Count: ${state.count}`)
])
);
const initReducer$ = xs.of( function initReducer() {
return {count: 0};
});
const updateReducer$ = action$.map( x => function reducer(prevState) {
return {count: prevState.count + x};
});
// this is where the inifinity loop starts
const historyReducer$ = popHistory$.map( history => function(prevState) {
return {count: history.state.count};
});
// You can't merge historyReducer$ here
const reducer$ = xs.merge(initReducer$, updateReducer$, historyReducer$);
const pushHistory$ = state$.map( state => ({
type: "push",
pathname: `/?count=${state.count}`,
state: state
}));
return {
DOM: vdom$,
onion: reducer$,
history: pushHistory$,
debug: popHistory$
}
};
const onionMain = onionify(main);
run(onionMain, {
DOM: makeDOMDriver("#main"),
history: makeHistoryDriver(),
debug: $ => $.subscribe({next: console.log})
});
I guess my question is: is there an easier way to do this?. Is there an operator that helps here? I feel like what I'm trying to do is fundamentally impossible. Any answers or links to useful resources would be appreciated.
Considering I figured this out, I thought I may as well post a minimal working example of routing with history / state with the onion state store. It may be useful for someone in the future as a reference. The answer was to filter out data flowing back to history from history popstates.
I guess the key with creating application behaviour that mimics the way server rendered webpages interact with history is that non of the mappings from history popstate flow back into history. Seems obvious now, but it took me a while to reason it out.