As a follow up to the Store lifecycle question,
In a typical web app its nice to have a shortcut to the current application state via the URL so you can re-visit that state and use the forward and back buttons to move between states.
With Flux we want all actions to go through the dispatcher, which i guess also include an URL change. how would you manage URL changes within a flux application?
What is flux? Flux uses a unidirectional data flow pattern to solve state management complexity. Remember it is not a framework – rather it's more of a pattern that targets to solve the state management issue.
Client side routing is a type of routing where as the user navigates around the application or website no full page reloads take place, even when the page's URL changes. Instead, JavaScript is used to update the URL and fetch and display new content.
Flux is the application architecture that Facebook uses for building client-side web applications. It complements React's composable view components by utilizing a unidirectional data flow. It's more of a pattern rather than a formal framework, and you can start using Flux immediately without a lot of new code.
In Flux application, data flows in a single direction(unidirectional). This data flow is central to the flux pattern. The dispatcher, stores, and views are independent nodes with inputs and outputs. The actions are simple objects that contain new data and type property.
[Update]
After working on a bunch of React/flux applications, I've come to the conclusion that I prefer for routing to be handled separately and orthogonally to flux. The strategy is that the URL/routes should determine which components get mounted, and the components request data from the stores based on the route parameters and other application state as necessary.
[Original Answer]
An approach I took with a recent project while experimenting with Flux was to make the routing layer just another store. This means that all links that change the URL actually trigger an action through the dispatcher requesting that the route be updated. A RouteStore
responded to this dispatch by setting the URL in the browser and setting some internal data (via route-recognizer) so that the views could query the new routing data upon the change
event being fired from the store.
One non-obvious piece for me was how to ensure URL changes triggered actions; I ended up creating a mixin to manage this for me (note: this isn't 100% robust, but worked for the app I was using; you may have to make modifications to suit your needs).
// Mix-in to the top-level component to capture `click` // events on all links and turn them into action dispatches; // also manage HTML5 history via pushState/popState var RoutingMixin = { componentDidMount: function() { // Some browsers have some weirdness with firing an extra 'popState' // right when the page loads var firstPopState = true; // Intercept all bubbled click events on the app's element this.getDOMNode().addEventListener('click', this._handleRouteClick); window.onpopstate = function(e) { if (firstPopState) { firstPopState = false; return; } var path = document.location.toString().replace(document.location.origin, ''); this.handleRouteChange(path, true); }.bind(this); }, componentWillUnmount: function() { this.getDOMNode().removeEventListener('click', this._handleRouteClick); window.onpopstate = null; }, _handleRouteClick: function(e) { var target = e.target; // figure out if we clicked on an `a` tag while(target && target.tagName !== 'A') { target = target.parentNode; } if (!target) return; // if the user was holding a modifier key, don't intercept if (!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) { e.preventDefault(); var href = target.attributes.href.value; this.handleRouteChange(href, false); } } };
It would be used as so:
var ApplicationView = React.createClass({ mixins: [RoutingMixin], handleRouteChange: function(newUrl, fromHistory) { this.dispatcher.dispatch(RouteActions.changeUrl(newUrl, fromHistory)); }, // ... });
The handler in the store might look something like:
RouteStore.prototype.handleChangeUrl = function(href, skipHistory) { var isFullUrl = function(url) { return url.indexOf('http://') === 0 || url.indexOf('https://') === 0; } // links with a protocol simply change the location if (isFullUrl(href)) { document.location = href; } else { // this._router is a route-recognizer instance var results = this._router.recognize(href); if (results && results.length) { var route = results[0].handler(href, results[0].params); this.currentRoute = route; if (!skipHistory) history.pushState(href, '', href); } this.emit("change"); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With