Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot get reference to Router before rendering and can't add store without router reference #132

Open
tachang opened this issue Aug 31, 2015 · 2 comments

Comments

@tachang
Copy link

tachang commented Aug 31, 2015

I am trying to add a RouteStore but can't seem to get the router reference. I am running into issues where the Login component renders and detects the user is already logged in and shoots off an action to redirect but the Router store is not ready to receive it yet.

router = React.render((
  <Router history={new HashHistory} createElement={createFluxComponent}>
    <Route path="/" component={App}>
      <Route path="login" component={Login} />
      <Route path="logout" component={Logout} />
      <Route path="about" component={About} />
      <Route path="dashboard" component={Dashboard} onEnter={requireAuth} />
    </Route>
  </Router>
), document.getElementsByClassName('app')[0]);

// Need to add RouteStore here because I need a reference to router
flux.addStore("RouteStore", new RouteStore({router: router}));

When the Login component mounts though I do a check:

  componentWillMount: function() {

    var flux = this.getFlux();
    if( this.state.session ) {
      flux.actions.route.transition("dashboard");
    }
  },

However the Login component mounts before the Store can be added so I get

An action of type ROUTE_TRANSITION was dispatched, but no store handled it
@BinaryMuse
Copy link
Owner

I'm not sure a route store is really the correct technique in most cases; the React Router example in the Fluxxor docs utilizes it mainly because models are not created asynchronously in action creators, unlike a more traditional Fluxxor app. Perhaps this sets a bad precedent.

Additionally, in React Router 1.0 beta, I'm not sure there's a way to access the router instance before the React rendering flow (via this.context), unlike in previous versions.

I think a more common technique, especially for situations like yours, would be to simply access the router on this.context to transition the URL (or just use the Navigation mixin):

  componentWillMount: function() {
    if( this.state.session ) {
      this.context.router.transitionTo("/dashboard");
    }
  },

  contextTypes: {
    router: React.PropTypes.object
  },

If you really want/need to utilize a store for routing operations, and just can't get ahold of the router instance, you might consider passing it along from the component:

  componentWillMount: function() {
    if( this.state.session ) {
      flux.actions.route.transition(this.context.router, "dashboard");
    }
  },

  contextTypes: {
    router: React.PropTypes.object
  },

Another (very hacky) workaround to the don't-have-the-router-instance-yet is to set it first thing from the componentWillMount of the very top-level component; something along the lines of:

  // In the App component
  componentWillMount: function() {
    var flux = this.getFlux();
    if (!flux.store("RouteStore")) {
      flux.addStore("RouteStore", new RouteStore({router: this.context.router}));
    }
  },

  contextTypes: {
    router: React.PropTypes.object
  },

Finally, I'm still not convinced the technique I originally used for the React Router example (before changing it) is actually that bad a practice; generally actions should be fire-and-forget, but for very side-effecty things like routing I'm not as much of a stickler:

  doThing: function() {
    flux.actions.thing.create(this.state.data, function(id) {
      this.context.router.transitionTo("/things/" + id);
    }.bind(this));
  },

  contextTypes: {
    router: React.PropTypes.object
  },

@BinaryMuse
Copy link
Owner

In React Router 1.0 RC1, they show how you can get the history object:

Locations

Locations are now called histories (that emit locations). You import
them from the history package, not react router.

// v0.13.x
Router.run(routes, Router.BrowserHistory, (Handler) => {
  React.render(<Handler/>, el);
})

// v1.0
import createBrowserHistory from 'history/lib/createBrowserHistory'
let history = createBrowserHistory()
React.render(<Router history={history}>{routes}</Router>, el)

This is analagous to getting a reference to the router before; You can use the history API to change the page. See also the API described at "Navigation Mixin".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants