Does anyone have any tips on building an authentication container that can wrap a react-meteor-data
container? Or, just how to wrap one container with another with react-router.
I just posted something that might help over here:
Additionally, here’s my (in progress) router.js. I’m using bootstrap and the associated bootstrap-accounts-ui to handle the actual login/logout right now.
import React from 'react';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import AppContainer from '../containers/AppContainer.jsx';
import EventsTable from '../components/EventsTable.jsx';
import Ledger from '../components/Ledger.jsx';
import { NotFound } from '../components/NotFound.jsx';
export default (
<Router history={browserHistory}>
<Route path="/" component={ AppContainer }>
<IndexRoute component={ EventsTable } />
</Route>
<Route path="*" component={ NotFound }>
</Route>
</Router>
);
Cheers
I did some more work on this after I realized that my ‘logged out’ state bypassed the router, leaving me with no route handling other than /. So, I tied the Router instantiation to the Meteor.user results, using separate route files for the loggedin/loggedout states. The react router generally refuses to ‘hot’ reload once it has been instantiated, but I found a technique to force a new instance (and therefore be able to use a completely different routes file whenever I want) by passing a unique key prop to the router instantiation like this:
uniqueKey = Date.now()
<Provider store={Store}>
<Router key={uniqueKey} history={browserHistory}>
{whateverRoutesIWant}
</Router>
</Provider>
So I moved the logic that waits for meteor.user() to come back valid to a parent container for my app, and this is also where my router and redux instantiation happens. I haven’t noticed any negative side effects yet.
So my component chain now looks like this:
client/main.jsx (hooks the top-level React component to the DOM)
|
|
App_SuperContainer.jsx (meteor data container that provides Meteor.user()
| results as a prop that reactively updates)
|
|
App_Super.jsx (Instantiates the redux store and react-router
instance based on logic that uses Meteor.user() prop)
|
|
App.jsx (contains the main presentation component and
renders children supplied by react-router)
|
|
Navbar.jsx, body components
The only time App_Super is rerendered, triggering a reinstiation of the Router, is when a user signs in, signs out, or when a manual page reload occurs. I can post complete source files if anyone is interested.
-Jerimiah
I’d definitely be interested in seeing what you did. I have a very similar requirement where nearly all of my routes are entirely dependent on if the user is signed in or not and currently the only way I can handle it is putting routing logic in each component. I get the basic idea of what you did and kind of have it working, but fitting in the redux piece with it is where I am struggling. Thanks!
Hi! Do you can post full source of your solution? I can’t reproduce this mechanics:
Warning: [react-router] You cannot change <Router routes>; it will be ignored
chef does this in his base repo:
Here’s the source files for how I did it. For the record, I’m not a professional meteor developer and have never deployed a meteor app to a production environment. I’m just playing around. This is probably a total hack and it may not even work for more than one user at a time. I honestly haven’t tested that. Proceed with caution.
client/main.jsx
import React from 'react';
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import '../imports/startup/accounts-config.js';
import App_SuperContainer from '../imports/client/containers/App_SuperContainer.jsx';
function AppRoot() {
return (
<App_SuperContainer/>
)
}
Meteor.startup(() => {
render(<AppRoot />, document.getElementById('render-target'));
});
App_SuperContainer.jsx
import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import AppSuper from '../components/App_Super.jsx';
export default createContainer(({ params }) => {
const currentUser = Meteor.user();
return {
currentUser,
};
}, AppSuper);
App_Super.jsx
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import Store from '../store/store.js';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import authRoutes from '../router/authRouter.jsx';
import Routes from '../router/router.jsx';
import { Events } from '../../api/events.js';
import { Meteor } from 'meteor/meteor';
export default class AppSuper extends Component {
render(){
//console.log("this.props.currentUser: "+this.props.currentUser);
console.log("Rendering the SuperContainer")
var userDataAvailable = true
var currentUser = this.props.currentUser
if (currentUser === undefined) {
userDataAvailable = false
}
var loggedOut = (!currentUser && userDataAvailable);
var loggedIn = (currentUser && userDataAvailable);
if (loggedIn) {
console.log("Logged in. Refreshing the router...")
console.log(currentUser)
let cachebuster = Date.now()
return(
<Provider store={Store}>
<Router key={cachebuster} history={browserHistory}>
{authRoutes}
</Router>
</Provider>
)
}
if (loggedOut) {
console.log("Logged out. Refreshing the router...")
let cachebuster = Date.now()
return(
<Provider store={Store}>
<Router key={cachebuster} history={browserHistory}>
{Routes}
</Router>
</Provider>
)
}
console.log("Still waiting for usable userdata")
return(
<div></div>
)
}
}
App.jsx
import React, { Component } from 'react';
import { Meteor } from 'meteor/meteor';
export default class App extends Component {
render() {
//console.log(this.props)
return (
<div>
{this.props.nav}
<div>
<div className="col-fixed">
{this.props.sidebar}
</div>
<div className="row">
<div className="col-md-12">
{this.props.content}
</div>
</div>
</div>
</div>
);
}
}
router.jsx (logged out)
import React, {Component} from 'react';
import { Router, Route, Redirect, browserHistory, IndexRoute } from 'react-router';
import App from '../components/App.jsx';
import Hero from '../components/Hero.jsx';
export default (
<Route path="/" component={ App }>
<IndexRoute loggedIn={false} components={{ nav: null, content: Hero }}></IndexRoute>
<Redirect from="*" to="/" />
</Route>
);
authRouter.jsx (logged in)
import React, {Component} from 'react';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import App from '../components/App.jsx';
import EventsTable from '../components/EventsTable.jsx';
import Navbar from '../components/Navbar.jsx';
import Sidebar from '../components/Sidebar.jsx';
import Ledger from '../components/Ledger.jsx';
import About from '../components/About.jsx';
import Hero from '../components/Hero.jsx';
import NotFound from '../components/NotFound.jsx';
export default (
<Route loggedIn={true} component={ App }>
<Route path="/" components={{ nav: Navbar, content: Hero }} />
<Route path="about" components={{ nav: Navbar, sidebar: Sidebar, content: About }} />
<Route path="events" components={{ nav: Navbar, sidebar: Sidebar, content: EventsTable }} />
<Route path="ledger" components={{ nav: Navbar, sidebar: Sidebar, content: Ledger }} />
</Route>
);