sorry for my poor English..my question is,when i use <Link/> to change current route into next route,how i can get previous path in next route's handler? here is some code
// route config
<Route handler={Route2} name="route2" path="route2" />
// eg: current location is #/route1?key=value
<Link to="route2">click to jump to route2</Link>
// route2
var Route2 = React.createClass({
componentDidMount: function() {
// how i can get previous path '#/route1?key=value'
},
render: function() {
return (
<div />
)
}
})
thx ~ :-)
Apparently there's no sanctioned way to do that.
As suggested in this issue, you can save the previous path in router.run.
var prevPath = null;
router.run(function(Handler, state) {
React.render(<Handler prevPath={prevPath} />, document.body);
prevPath = state.path;
});
Beware, however, that this will point "forward" after using the browser back button.
Related
In my project I deiced to include React Router. One of my Reflux Stores need to figure out the path based on some BL and than change it. First I've tried including the Navigation mixin inside the Store and running this.transitionTo("Foo"); which threw an error : "Uncaught TypeError: Cannot read property 'router' of undefined".
Someone suggested that : "this.transitionTo is probably accessing the router property through contexts (this.context.router) which do not exist in RefluxJS stores" ... Which I understand.
However there must be a way to change the router path from a Store Programmatically, or any given external JS Module.
My Code goes something like this:
// routes.js
//////////////////////////////////////////////////////////
var Router = require('react-router');
var Route = Router.Route;
var App = require('./app');
var Comp = require('./components/comp');
var routes = (
<Route path='/' handler={App}>
<Route name='Foo' path='/foo' handler={Comp}/>
</Route>
);
module.exports = routes;
// main.js
//////////////////////////////////////////////////////////
var React = require('react');
var Router = require('react-router');
var routes = require('./Routes');
var appElement = document.getElementsByTagName('body');
Router.run(routes, Router.HistoryLocation, function(Root, state) {
React.render(<Root params={state.params} query={state.query} />, appElement);
});
// comp.js
//////////////////////////////////////////////////////////
var React = require("react");
var Reflux = require("reflux");
var Actions = require("../actions/actions.js");
var SomeStore = require("../stores/some-store.js");
var Comp = React.createClass({
render: function() {
return (
<h1>Hello World</h1>
);
}
});
module.exports = Comp;
// store.js
//////////////////////////////////////////////////////////
var SomeStore = Reflux.createStore({
onSomeAction: function() {
// CHANGE ROUTER PATH HERE TO /foo
}
});
module.exports = SomeStore;
Any help will be appreciated!
The router is only known by the components (React views). You need to pass the router from the context as a parameter in your action. That way, you can use this parameter to make a transition to a different route. I've been using it this way.
I have something like below in one of my action listeners in a store.
_onMyAction: function (router) {
MyApi.doSomething()
.then(function (id) {
// do something ...
router.transitionTo('myNewRoute', { ref: id });
})
.catch(function(message) {
// handle message
});
}
Reflux actions return a promise, so rather than doing this in the store (which IMO is wrong) you can do it in your component:
Actions.someAction().then(function() {
// route transition
});
Whether this is completely right... well, I'm not sure the community has really settled on an opinion.
I am writing a authentication module in Flux, actions : auth,auth_success,auth_error. I am thinking when action auth_error occur, the router will go to '/login'. When action, action, auth_success occur, the router will go to '/dashboard'.
But it seems to be wrong because action only goes to dispatcher. I don't know how to do route the callbacks. Maybe check the store value?
You have to mixin your React class with Route.Navigation object, for instace
/** #jsx React.DOM */
var React = require('react');
var Router = require('react-router')
, Navigation = Router.Navigation;
var UserStore = require('user-store');
var YourClass = module.exports = React.createClass({
mixins:[Navigation], //This is very important
getInitialState: function() {
return {};
},
componentWillMount: function(){
UserStore.addChangeListener(this._method);
},
componentWillUnmount: function(){
UserStore.removeChangeListener(this._method);
},
render: function() {
return (
<div>
</div>
);
},
_method: function() {
// From here you can call methods of Navigator Object
this.transitionTo('SomeRouteName'); //This method will render the specified <Route>
}
});
For further information you can check
https://github.com/rackt/react-router/blob/master/docs/api/mixins/Navigation.md
In order to change the route and according to flux architecture, you should call transitionTo from a callback of some User Store you should have.
I added an example to the code, you may customise it to your specific case.
Happy coding!
I'm having a problem with react-router-component. I'm trying to make a redirect library that does a "soft" redirect (equivalent to clicking a link, as opposed to window.location = ...) that I can require from a .jsx component.
Per the docs I should just be able to call router.navigate(path) to redirect.
When, for example, I call this on page A, the url in the address bar changes to page B as desired. However, page A simply reloads, leaving the address bar as page B and the display as page A. Suggestions?
You should be able to solve this using the Navigation mixin.
Then you can use this.transitionTo with the name of your page.
var PageAdd = React.createClass({
mixins : [Router.Navigation],
handleSubmit : function(e) {
e.preventDefault();
this.transitionTo('pages');
},
render: function () {
return (
<form onSubmit={this.handleSubmit}>
<button type="submit" className="btn btn-default">Submit</button>
</form>
);
}
});
Read more: https://github.com/rackt/react-router/blob/master/docs/api/mixins/Navigation.md
We ended up doing a hacky solution: calling the following when a redirect should happen, where path is something like /questions/23.
// call an event to get the router from somewhere else that's listening
// to this event specifically to provide the router
document.dispatchEvent(new CustomEvent('router.get', {
detail: {
callback: function(router) {
router.navigate(path);
}
}
}));
Then, in the router, the event listener:
var App = React.createClass({
listenForRouter: function listenForRouter () {
var self = this;
document.addEventListener('router.get', function(e) {
setTimeout(function() {
e.detail.callback(self
.refs.router
.refs.locations);
}, 1);
});
},
componentDidMount: function componentDidMount () {
this.listenForRouter();
},
// additional lifecycle methods
});
Not sure if this really answers the question, and an answer has already been accepted but I would use link from react-router:
in the routes you define names (this from a music database page:
var routes = (
<Route name="app" path="/" handler={require('./components/app')}>
<DefaultRoute handler={require('./components/homePage')}/>
<Route name="artist" handler={require('./components/artist/artistsPage')}/>
<Route name="artistSearch" path="artist/:artistSearchName" handler={require('./components/artist/artistsPage')}/>
<Route name="artistSearchPage" handler={require('./components/artist/manageArtistSearchPage')}/>
<Route name="album" handler={require('./components/album/albumsPage')}/>
<Route name="searchAlbums" path="album/:artistId" handler={require('./components/album/albumsPage')}/>
<Route name="song" handler={require('./components/song/songsPage')}/>
<NotFoundRoute handler={require('./components/pageNotFound')}/>
</Route>
);
and then in your component import Link from React-router:
var Link = require('react-router').Link; //(ES5)
import {link as Link} from 'react-router'; //(ES6)
and then you can use as the tag and it will work like an a tag with href = "something"
<div>
<h1>Artist Search Page</h1>
<ArtistSearchForm
artistName={this.state.artistName}
onChange={this.onArtistChangeHandler}
/>
<br/>
<Link to="artistSearch" params={{artistSearchName: this.state.artistName}} className="btn btn-default">Search</Link>
</div>
I also used it in a list to make the artist name a link to there albums page.
This is not well-documented, but it is possible to perform a soft redirect using the navigate(path) method of the environment.defaultEnvironment object.
import {environment} from 'react-router-component'
...
environment.defaultEnvironment.navigate("/");
This doesn't take into account the current routing context though, so it won't allow you to redirect to paths relative to inner routers. For an approach that respects the routing context, see NavigatableMixin. Mixins are not an option for my solution, since I'm using ES6 classes, but the implementation could be copied from there.
Let's say I have one root Ractive on the page,and various widgest to show when an hypothetic backbone router navigate to a route :
var widget1=Ractive.extend({template:"<div>{{foo}}</div>"});
var widget2=Ractive.extend({template:"<div>{{bar}}</div>"});
var view=new Ractive({
template:"<nav></nav><widget />",
components:{widget:widget1}
});
var Router=Backbone.Router.extend({/* the code ... */})
so widget1 would be shown when I navigate to /widget1 and widget2 when the route is /widget2,
What would be the best way to swap widgets depending on the current route without creating seperate root Ractives or hiding/showing widgets? thanks.
An alternative solution to my previous suggestion, which allows routes to be set in a more dynamic fashion (i.e. without having to declare them in a template up-front):
<nav>...</nav>
<!-- we have a single <route> component representing all possible routes -->
<route current='{{currentRoute}}'/>
This could be implemented like so:
Ractive.components.route = Ractive.extend({
template: '<div class="container"></div>',
init: function () {
this.container = this.find( '.container' );
this.observe( 'current', function ( currentRoute ) {
var View = routes[ currentRoute ];
if ( this.view ) {
this.view.teardown();
}
this.view = new View({
el: this.container
});
});
}
});
Then, to switch routes:
router.on( 'route', function ( route ) {
ractive.set( 'currentRoute', route );
});
With this approach all you'd need to do is register all the possible routes at some point in your app:
routes.widget1 = Ractive.extend({template:"<div>{{foo}}</div>"});
...and so on. If necessary you could interact with each view object by retrieving a reference:
route = ractive.findComponent( 'route' );
route.view.on( 'someEvent', someEventHandler );
One way would be to explicitly include the components representing each route in the top-level template:
<nav>...</nav>
{{# route === 'widget1' }}
<widget1/>
{{/ route === 'widget1' }}
{{# route === 'widget2' }}
<widget2/>
{{/ route === 'widget2' }}
Then, you could do something like:
router.on( 'route', function ( route ) {
ractive.set( 'route', route );
});
This would tear down the existing route component and create the new one.
If your route components had asynchronous transitions, you could ensure that the new route didn't replace the old route until any transitions had completed by doing this:
router.on( 'route', function ( route ) {
ractive.set( 'route', null, function () {
ractive.set( 'route', route );
});
});
(Note that as of version 0.4.0, ractive.set() will return a promise that fulfils when any transitions are complete - though callbacks will still be supported.)
Having said all that I'd be interested to hear about any other patterns that people have had success with.
Have done some working samples using Backbone Router, but is there a way to protect the routes being used directly on the address bar? And also when the user press the back button on the browser, the routes doesn't get cleared and creates issues. What is the best solution for this?
I think I see what you're saying - you want to force the user to enter your site through a certain (home) page. Is that correct?
This is useful, for example, when you're building a mobile-optimized-web-app, and you always want users to enter through a splash screen. What I'll do is set a 'legitEntrance' property to my router, and check for it on every route, as so:
APP.Router = Backbone.Router.extend({
legitEntrance: false,
// Just a helper function
setLegitEntrance: function() {
this.legitEntrance = true;
},
// Send the user back to the home page
kickUser: function() {
this.navigate("home", {trigger:true});
},
routes : {
...
},
// Example router function: Home page
routeToHome: function() {
this.setLegitEntrance();
var homeView = APP.HomeView.extend({ ... });
homeView.render();
},
// Example router function: some other internal page
routeToSomeOtherInternalPage: function() {
if(!this.legitEntrance) {
this.kickUser();
return;
}
var someOtherInternalView = APP.SomeOtherInternalView.extend({
...
});
someOtherInternalView.render();
}
....
});
I'm sure this code could be cleaned up some, but you get the general idea. Hope it helps.