React-redux: Access store from routes file without using `connect`

Created on 7 Mar 2016  路  7Comments  路  Source: reduxjs/react-redux

I would like to make use of react-router's onEnter handler in order to prompt users to authenticate when entering a restricted route.

So far my routes.js file looks something like this:

import React from 'react';
import { Route, IndexRoute } from 'react-router';

export default (
    <Route   path="/"         component={App}>
      <IndexRoute             component={Landing} />
      <Route path="learn"     component={Learn} />
      <Route path="about"     component={About} />
      <Route path="downloads" component={Downloads} onEnter={requireAuth} />
    </Route>
)

Ideally, I'd like my requireAuth function to be a redux action that has access to the store and current state, that works like this: store.dispatch(requireAuth()).

Unfortunately I don't have access to the store in this file. I don't think I can use really use connect in this case to access the relevant actions that I want, because the thing I'm exporting is not a component. I also can't just import store from the file where the store is created, as this is undefined when the app first loads.

Most helpful comment

Thank you @gaearon!! It's nice to be able to still have routes in a separated file.

BTW, I really like your Redux training on egghead.io. I really enjoy the way you describe the idea, then do a simple implementation, and then finally add layers of abstractions at the end. It's the best training video I've seen in the last several years. Thank you!

All 7 comments

@Robinnnnn I have similar need that I want to access store in onEnter. How did you solve your problem?

hey @jungejason, I just decided to pass down the store as props into the component that was generating my routes. Here is my code, in full:

import React, {PropTypes} from 'react';
import {Provider}         from 'react-redux';
import {Router, browserHistory, Route, IndexRoute, Redirect} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';

import App                      from './components/App';
import Landing              from './components/routes/public/Landing';
import Prospect             from './components/routes/public/Prospect';
import Mobile               from './components/routes/public/Mobile';
import { Learn }            from './components/routes/public/Learn';
import { About }            from './components/routes/public/About';
import OpenPositions    from './components/routes/public/Careers/OpenPositions';
import Job              from './components/routes/public/Careers/Job';
import Downloads            from './components/routes/restricted/Downloads';
import ResetPassword    from './components/routes/public/ResetPassword';
import PanoLibrary      from './components/routes/restricted/PanoLibrary';
import NoMatch              from './components/routes/public/NoMatch';
import Project              from './components/routes/restricted/PanoLibrary/Project';
import {requireAuth}    from './actions/auth/requireAuth';


export default class Root extends React.Component {
    render() {
        const { store } = this.props; // *** passed here from the parent component.
        const history = syncHistoryWithStore(browserHistory, store)

        // here is the function that is triggered onEnter
        const requireAuthentication = (nextState, replace) => {
            store.dispatch(requireAuth(nextState, replace, store));
        }

        return (
            <div>
              <Provider store={store}>
                <Router history={history}>
                  <Route   path="/"                 component={App}                   >
                    <IndexRoute                 component={Landing}          />
                    <Route path="prospect"  component={Prospect}         />
                    <Route path="mobile"      component={Mobile}             />
                    <Route path="learn"       component={Learn}              />
                    <Route path="about"       component={About}              />
                    <Route path="jobs">
                        <IndexRoute                     component={OpenPositions}/>
                      <Route path=":id"         component={Job}                  />
                    </Route>
                    <Route path="downloads" component={Downloads}   onEnter={requireAuthentication}/>
                    <Route path="panos"       component={PanoLibrary} onEnter={requireAuthentication}   >
                        <IndexRoute                     component={Project}/>
                    </Route>
                    <Route path="reset-password/:token" component={ResetPassword}/>
                    <Route path="nomatch"                           component={NoMatch}          />
                    <Redirect from="*" to="nomatch"/>
                  </Route>
                </Router>
              </Provider>
            </div>
        )
    }
}

And here is the parent file:

import React                          from 'react';
import { render }             from 'react-dom';
import Root                         from './Root';
import { configureStore }   from './store';
import { storeInitialLocation, examineLocalStorage } from './actions';
export const store = configureStore(window.__INITIAL_STATE__);

render(
  // passing the store down to the component that renders my routes
  <Root store={store} />,
  document.getElementById('app')
)

Thank you @Robinnnnn!!

Alternatively you can change your routes.js to export a function like getRoutes(store) and pass it there.

Thank you @gaearon!! It's nice to be able to still have routes in a separated file.

BTW, I really like your Redux training on egghead.io. I really enjoy the way you describe the idea, then do a simple implementation, and then finally add layers of abstractions at the end. It's the best training video I've seen in the last several years. Thank you!

@jungejason Thank you for kind words!

The ability to abstract routes.js is super helpful.

On another note, I side with @jungejason -- thank you for all that you do @gaearon, your egghead videos are the reason I decided to migrate our web stack to React + Redux and away from Angular. I'm a much happier person now :)

Was this page helpful?
0 / 5 - 0 ratings