2016-03-07 7 views
34

Ich möchte den Handler onEnter von react-router verwenden, um die Benutzer zur Eingabe einer eingeschränkten Route zu veranlassen.Zugriff auf Redux Store von Routen, die über React Router eingerichtet wurden

Bisher meine routes.js Datei sieht in etwa wie folgt aus:

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> 
) 

Im Idealfall würde ich meine requireAuth Funktion wie ein Redux Aktion zu sein, die Zugriff auf den Speicher und den aktuellen Status hat, die wie folgt funktioniert: store.dispatch(requireAuth()).

Leider habe ich keinen Zugriff auf den Speicher in dieser Datei. Ich glaube nicht, dass ich wirklich connect in diesem Fall verwenden kann, um auf die relevanten Aktionen zuzugreifen, die ich möchte. Ich kann nicht nur import store aus der Datei, in der der Speicher erstellt wird, da dies nicht definiert ist, wenn die App zuerst lädt.

Antwort

53

Der einfachste Weg, dies zu erreichen, besteht darin, Ihren Speicher an eine Funktion zu übergeben, die Ihre Routen zurückgibt (anstatt Ihre Routen direkt zurückzugeben). Auf diese Weise können Sie auf den Shop in onEnter und andere reagieren Router-Methoden zugreifen.

Also für Ihre Routen:

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

export const getRoutes = (store) => (
    const authRequired = (nextState, replaceState) => { 
    // Now you can access the store object here. 
    const state = store.getState(); 

    if (!state.user.isAuthenticated) { 
     // Not authenticated, redirect to login. 
     replaceState({ nextPathname: nextState.location.pathname }, '/login'); 
    } 
    }; 

    return (
    <Route path="/"   component={App}> 
     <IndexRoute    component={Landing} /> 
     <Route path="learn"  component={Learn} /> 
     <Route path="about"  component={About} /> 
     <Route path="downloads" component={Downloads} onEnter={authRequired} /> 
    </Route> 
); 
) 

dann Ihre Hauptkomponente aktualisieren, um die getRoutes Funktion aufzurufen, im Laden vorbei:

<Provider store={ store }> 
    <Router history={ history }> 
    { getRoutes(store) } 
    </Router> 
</Provider> 

Wie für eine Aktion aus requireAuth Disposition, könnten Sie schreiben Ihre Funktion ist wie folgt:

const authRequired = (nextState, replaceState, callback) => { 
    store.dispatch(requireAuth()) // Assume this action returns a promise 
    .then(() => { 
     const state = store.getState(); 

     if (!state.user.isAuthenticated) { 
     // Not authenticated, redirect to login. 
     replaceState({ nextPathname: nextState.location.pathname }, '/login'); 
     } 

     // All ok 
     callback(); 
    }); 
}; 

Hoffnung das hilft.

+0

das ist ein gutes Beispiel. Vielen Dank :) – shivam

+1

Wie würde das mit Server-Rendering funktionieren? – Enkay

+0

TY einiges, dieser Ansatz ist wirklich einfach Tom implementieren, aber ich brauche fragen, gibt es irgendwelche Rückzieher mit diesem? – StormRage

12

Wenn Sie möchten, dass Sie route.js so schreiben könnte:

var requireAuth = (store, nextState, replace) => { 
    console.log("store: ", store); 
    //now you have access to the store in the onEnter hook! 
} 

export default (store) => { 
    return (
     <Route path="/"   component={App}> 
     <IndexRoute    component={Landing} /> 
     <Route path="learn"  component={Learn} /> 
     <Route path="about"  component={About} /> 
     <Route path="downloads" component={Downloads} onEnter={requireAuth.bind(this, store)} /> 
     </Route> 
    ); 
); 

Ich habe Setup ein Beispiel, das Sie mit in diesem codepen spielen könnte.

Nicht sicher, ob das Auslösen einer Aktion zum Behandeln der Authentifizierung eine gute Idee ist. Persönlich bevorzuge ich die Handhabung auth in eine andere Art und Weise:

Anstatt einen onEnter Haken zu verwenden, verwende ich eine Verpackungsfunktion. Ich möchte den Admin-Bereich meines Blogs geschützt, daher habe ich die AdminContainer Komponente in den Routen mit einer Funktion, requireAuthentication, umschlossen, siehe unten.

export default (store, history) => { 
     return (
      <Router history={history}> 
       <Route path="/" component={App}> 
        { /* Home (main) route */ } 
        <IndexRoute component={HomeContainer}/> 
        <Route path="post/:slug" component={PostPage}/> 
        { /* <Route path="*" component={NotFound} status={404} /> */ } 
       </Route> 

       <Route path="/admin" component={requireAuthentication(AdminContainer)}> 
        <IndexRoute component={PostList}/> 
        <Route path=":slug/edit" component={PostEditor}/> 
        <Route path="add" component={PostEditor}/> 
       </Route> 
       <Route path="/login" component={Login}/> 
      </Router> 
     ); 
    }; 

requireAuthentication ist eine Funktion, die

  • , wenn der Benutzer, die verpackte Komponente rendert authentifiziert ist,
  • Umleitungen auf andere Weise Login

Sie können es unten sehen:

export default function requireAuthentication(Component) { 
    class AuthenticatedComponent extends React.Component { 

     componentWillMount() { 
      this.checkAuth(); 
     } 

     componentWillReceiveProps (nextProps) { 
      this.checkAuth(); 
     } 

     checkAuth() { 
      if (!this.props.isAuthenticated) { 
       let redirectAfterLogin = this.props.location.pathname; 
       this.context.router.replace({pathname: '/login', state: {redirectAfterLogin: redirectAfterLogin}}); 
      } 
     } 

     render() { 
      return (
       <div> 
        {this.props.isAuthenticated === true 
         ? <Component {...this.props}/> 
         : null 
        } 
       </div> 
      ) 

     } 
    } 

    const mapStateToProps = (state) => ({ 
     isAuthenticated: state.blog.get('isAuthenticated') 
    }); 

    AuthenticatedComponent.contextTypes = { 
     router: React.PropTypes.object.isRequired 
    }; 

    return connect(mapStateToProps)(AuthenticatedComponent); 
} 

Außerdem schützt requireAuthentication alle Routen unter /admin. Und Sie können es überall wiederverwenden.

+0

Scheint wie AuthenticatedComponent ist die Verwendung der visuellen React-Komponente, die für nicht-visuelle Routenauthentifizierungszwecke verwendet wird. Denkst du nicht, dass all diese KomponentenWillMount nicht die Route Auth überprüfen überhaupt? –

+0

Ich stimme @ alex_1948511, es ist ein Hack. Aber andererseits sind sehr wenige Dinge in der JS-Welt gut definiert (oder vielleicht ist es nur meine Ansicht als ein n00b in der JS-Programmierung). Ich bin offen für irgendwelche Vorschläge, wie man das in React besser macht. Ich habe das in den letzten Monaten nicht untersucht, da ich diesen Ansatz irgendwo im Internet gefunden habe, habe ich nicht wirklich weiter gesucht. :-) –

+0

Ich kann hinzufügen, in Router v4 können Sie Router Tags nicht verschachteln. Dies wird einen Fehler auslösen –

2

Viele haben sich im Laufe der Zeit verändert. onEnter existiert nicht mehr auf react-router-4

Das Folgende ist aus meinem realen Projekt für Ihre Referenz

export const getRoutes = (store) => { 
 
    const PrivateRoute = ({ component: Component, ...rest }) => (
 
    <Route {...rest} render={props => (
 
     checkIfAuthed(store) ? (
 
     <Component {...props}/> 
 
    ) : (
 
     <Redirect to={{ 
 
      pathname: '/login' 
 
     }}/> 
 
    ) 
 
    )}/> 
 
) 
 

 
    return (
 
    <Router> 
 
     <div> 
 
     <PrivateRoute exact path="/" component={Home}/> 
 
     <Route path="/login" component={Login} /> 
 
     </div> 
 
    </Router> 
 
) 
 
}

Verwandte Themen