web-dev-qa-db-fra.com

Dans l'architecture Flux, comment gérez-vous les états de routage / URL côté client?

Pour faire suite à la Question sur le cycle de vie du magasin ,

Dans une application Web typique, il est agréable d'avoir un raccourci vers l'état actuel de l'application via l'URL afin que vous puissiez revisiter cet état et utiliser les boutons avant et arrière pour vous déplacer entre les états.

Avec Flux, nous voulons que toutes les actions passent par le répartiteur, qui, je suppose, inclut également un changement d'URL. comment géreriez-vous les changements d'URL dans une application de flux?

42
krs

[Mise à jour]

Après avoir travaillé sur un tas d'applications React/flux, je suis arrivé à la conclusion que je préfère que le routage soit traité séparément et orthogonalement au flux. La stratégie consiste à ce que l'URL/les itinéraires déterminent les composants à monter et les composants demandent des données aux magasins en fonction des paramètres de l'itinéraire et de l'état de l'application, si nécessaire.

[Réponse originale]

Une approche que j'ai prise avec un projet récent en expérimentant avec Flux était de faire de la couche de routage juste un autre magasin. Cela signifie que tous les liens qui modifient l'URL déclenchent réellement une action via le répartiteur demandant que la route soit mise à jour. Un RouteStore a répondu à cette répartition en définissant l'URL dans le navigateur et en définissant certaines données internes (via route-Recognizer ) afin que les vues puissent interroger les nouvelles données de routage sur le change événement déclenché depuis le magasin.

Un élément non évident pour moi était de savoir comment garantir que les changements d'URL déclenchent des actions; J'ai fini par créer un mixin pour gérer cela pour moi (note: ce n'est pas 100% robuste, mais a fonctionné pour l'application que j'utilisais; vous devrez peut-être apporter des modifications en fonction de vos besoins).

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

Il serait utilisé comme suit:

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

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

  // ...
});

Le gestionnaire dans le magasin pourrait ressembler à quelque chose comme:

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");
  }
}
43
Michelle Tilley

La plupart des exemples dans la nature utilisent React Router , un framework basé sur le Ember routeur. La partie importante est la configuration des routes comme spécification déclarative des composants:

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)
2
Nacho Coloma