2014-05-13 5 views
41

Per dare un seguito al Store lifecycle question,In architettura Flux, come gestite gli stati di routing/url lato client?

In una tipica applicazione web è bello avere un collegamento per lo stato dell'applicazione corrente tramite l'URL in modo da poter ri-visitare questo stato e utilizzare i pulsanti Avanti e Indietro per spostarsi tra stati.

Con Flux vogliamo che tutte le azioni passino attraverso il dispatcher, che suppongo includano anche una modifica dell'URL. come gestiresti le modifiche agli URL all'interno di un'applicazione di flusso?

+0

Potrebbe chiarire cosa intendi navigando tra gli stati nella tua domanda? Penso che tu non intenda lo stato dell'applicazione, ma piuttosto la navigazione tra route/URL di SPA. In tal caso, Flux è semplicemente un'architettura applicativa che descrive la comunicazione e il controllo tra le app. La community di Flux ha in genere deciso che il routing non rientra nello scope e vedere la risposta di Nacho: react router. –

risposta

43

[Update]

Dopo aver lavorato su un mucchio di Reagire/applicazioni di flusso, sono giunto alla conclusione che preferisco per il routing deve essere trattata separatamente e ortogonalmente al Flux. La strategia è che l'URL/i percorsi dovrebbero determinare quali componenti vengono montati e i componenti richiedono i dati dai negozi in base ai parametri del percorso e allo stato di altre applicazioni, se necessario.

[Risposta originale]

Un approccio ho preso con un recente progetto, mentre la sperimentazione di flusso era di rendere il livello di routing solo un altro negozio. Ciò significa che tutti i link che modificano l'URL attivano effettivamente un'azione attraverso il dispatcher richiedendo che l'itinerario venga aggiornato. A RouteStore ha risposto a questa spedizione impostando l'URL nel browser e impostando alcuni dati interni (tramite route-recognizer) in modo che le viste possano interrogare i nuovi dati di routing sull'evento change generato dall'archivio.

Un elemento non ovvio per me era come garantire le azioni attivate delle modifiche all'URL; Ho finito per creare un mixin per gestirlo per me (nota: questo non è robusto al 100%, ma ha funzionato per l'app che stavo usando, potrebbe essere necessario apportare modifiche in base alle proprie esigenze).

// 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); 
    } 
    } 
}; 

Sarebbe essere utilizzato come così:

var ApplicationView = React.createClass({ 
    mixins: [RoutingMixin], 

    handleRouteChange: function(newUrl, fromHistory) { 
    this.dispatcher.dispatch(RouteActions.changeUrl(newUrl, fromHistory)); 
    }, 

    // ... 
}); 

Il gestore del negozio potrebbe essere simile:

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"); 
    } 
} 
+2

Che ne dici di integrare il routing nel dispatcher da solo? Alcuni degli eventi saranno trasformati in un percorso. Quindi, quando qualcuno colpisce il percorso, il dispatcher può inviare l'evento relativo. –

+0

@BinaryMuse, qual è stata la ragione per cui sei arrivato alla tua conclusione aggiornata? Sto gestendo un'applicazione Angular più ampia che è abbastanza reattiva e sto passando a FLUX puramente allo scopo di cercare di ottenere un flusso di dati singolare. Sarebbe interessante sapere perché preferisci una soluzione diversa per il routing, dato che sono appena agli inizi. –

+0

@BryanRayner Gli archivi Flux prendono essenzialmente eventi (azioni) e li riducono a cambiamenti di stato, che sono memorizzati nei negozi. Il routing è fondamentalmente questo: gli eventi cronologici del browser sono le azioni e il negozio è l'URL corrente. Ho scoperto che aggiungere ulteriore complessità qui non sembra offrire alcun vantaggio. Inoltre, con tutto lo stato serializzabile nell'URL, è chiaro cosa viene salvato lì e cosa non lo è. –

2

maggior parte degli esempi nel fare uso selvaggia di React Router, un quadro basato sul router Ember. La parte importante è la configurazione dei percorsi come specifica dichiarativa dei componenti:

React.render((
    <Router> 
    <Route path="/" component={App}> 
     <Route path="about" component={About}/> 
     <Route path="users" component={Users}> 
     <Route path="/user/:userId" component={User}/> 
     </Route> 
     <Redirect from="/" to="about" /> 
     <NotFoundRoute handler={NoMatch} /> 
    </Route> 
    </Router> 
), document.body)