在Flux架构中,您如何管理客户端路由/ URL状态?

时间:2014-05-13 07:00:15

标签: reactjs reactjs-flux

作为Store lifecycle question的后续行动,

在一个典型的网络应用程序中,很高兴通过URL获得当前应用程序状态的快捷方式,这样您就可以重新访问该状态并使用前进和后退按钮在状态之间移动。

使用Flux,我们希望所有操作都通过调度程序,我想这也包括URL更改。您将如何管理助焊剂应用程序中的URL更改?

2 个答案:

答案 0 :(得分:43)

<强> [更新]

在研究了一堆React / flux应用程序之后,我得出的结论是,我更喜欢将路由分开处理并与通量正交。策略是URL /路由应该确定哪些组件被挂载,组件根据路由参数和其他应用程序状态从存储中请求数据。

[原始答案]

我在尝试使用Flux时最近的一个项目是将路由层设置为另一个商店。这意味着更改URL的所有链接实际上都会通过调度程序触发请求更新路由的操作。 RouteStore通过在浏览器中设置网址并设置一些内部数据(通过route-recognizer)来响应此调度,以便在change事件被触发时,视图可以查询新的路由数据来自商店。

对我来说,一个非显而易见的部分是如何确保URL更改触发的操作;我最终创建了一个mixin来为我管理这个(请注意:这不是100%强大,但是我正在使用的应用程序工作;您可能需要进行修改以满足您的需求。)

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

它将被用作:

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

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

  // ...
});

商店中的处理程序可能类似于:

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

答案 1 :(得分:2)

野外的大多数示例都使用React Router,这是一个基于Ember路由器的框架。重要的部分是将路由配置为组件的声明性规范:

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)