I've been googling and reading documentation and chatting on discord, but with no success.
I've got an existing app to which I need to add, on home page, couple of 'Sign Up' buttons that should be <Link> to a react component (I have to avoid full page reload and provide page history). These buttons would just display a react component in an overlay. It fails since the <Link> is being used outside of the router.
Any way I could provide the context outside of router? If I just reinit the router for every button, the router throws warning saying it's already been initialized.
I've seen @Andrew1431 was searching for something similar. Any luck @Andrew1431?
My app uses rails as a backend and I'm not even sure how much work would it be to try and put everything in a react component (since it's not just pure static HTML, but has dynamic rails stuff in there as well).
Some solutions that come to my mind:
Hey there! This issue was really frustrating to figure out, but I finally got a hack work around in our system.
We're developing our system in a package-esque manner, meaning we have a global name space storing all our services / utilities and what not. I discovered through reading the source code of the Link object that all it is doing is preventing the default click action, and pushing state in to the history object. My hackish work around is as follows until there is a better way:
Where our routes are defined I included the history object in to the global name space.
/* Include react-layout browser history */
const browserHistory = history.createHistory();
Core.Utilities.History = browserHistory;
render: function() {
return (
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={requireAuth}>
<IndexRoute key="0" component={this.getDefaultRoute()}/>
{this.state.routeService.Routes.map(function(r) {
return r;
})}
</Route>
</Router>
);
}
Then anywhere we needed Link elements out side of the router, I simply called the function the Link elements call:
let MenuItem = React.createClass({
displayName: 'MenuItem',
pushState(state) {
Core.Utilities.History.pushState(null, state);
closeMenu();
},
render() {
let item = this.props.data;
return <a className="item" onClick={() => {this.pushState(item.route)}}>{item.title}</a>
}
});
Ideally I wish the Link element would take an optional history prop that allowed you to inject your own history reference rather than it looking for the contexts history which won't exist when the Link element is out side of the Route context (Our case was semantic UI's forcing the side menu to be out side of the application)
I hope this helps, sorry its not exactly what you're looking for, but it functions exactly as I need it to.
Be sure to read the Link source code if there's any additional functionality you would need to mimic, or perhaps consider creating your own Link element that mimics this, but takes a history context prop.
https://github.com/rackt/react-router/blob/master/modules/Link.js
That's about right - the semantics of <Link> don't really make sense outside of the router (how can it evaluate active state if it's not in a route at all?), but you can replicate the behavior yourself without too much trouble.
Thanks @Andrew1431!
I'm not that familiar with react. Can you help me figure out the first snippet? On which object is the first render method defined?
Should I require history from 'history' package?
/* Include react-layout browser history */
const browserHistory = history.createHistory();
Core.Utilities.History = browserHistory;
render: function() {
return (
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={requireAuth}>
<IndexRoute key="0" component={this.getDefaultRoute()}/>
{this.state.routeService.Routes.map(function(r) {
return r;
})}
</Route>
</Router>
);
}
That should be the route object that is loaded in to the dom under react-routers docs. Here's the whole snippet we used.
/* Create the main Routes react component */
let Routes = React.createClass({
/* ... */
render: function() {
return (
<Router history={browserHistory}>
<Route path="/" component={App} onEnter={requireAuth}>
<IndexRoute key="0" component={this.getDefaultRoute()}/>
{this.state.routeService.Routes.map(function(r) {
return r;
})}
</Route>
</Router>
);
}
});
Meteor.startup(function () {
$(document).ready(function(){
ReactDOM.render(<Routes />, document.getElementById('app'));
ReactDOM.render(<SideNav />, document.getElementById('side-nav'));
});
});
All though our system is designed for dynamic route loading so this is much more complicated than it needs to be :P I hope that helps a little bit. My custom link items are in the SideNav object out side of app element that I render Routes in to, and I'm too lazy to draw up an example to give you :)
history object somewhere in the apphistory.pushState(...) anywhere you want.Thanks guys, it worked! :)
Most helpful comment
Hey there! This issue was really frustrating to figure out, but I finally got a hack work around in our system.
We're developing our system in a package-esque manner, meaning we have a global name space storing all our services / utilities and what not. I discovered through reading the source code of the Link object that all it is doing is preventing the default click action, and pushing state in to the history object. My hackish work around is as follows until there is a better way:
Where our routes are defined I included the history object in to the global name space.
Then anywhere we needed Link elements out side of the router, I simply called the function the Link elements call:
Ideally I wish the Link element would take an optional history prop that allowed you to inject your own history reference rather than it looking for the contexts history which won't exist when the Link element is out side of the Route context (Our case was semantic UI's forcing the side menu to be out side of the application)
I hope this helps, sorry its not exactly what you're looking for, but it functions exactly as I need it to.