2016-03-07 25 views
34

Desidero utilizzare il gestore onEnter del react-router per richiedere agli utenti di autenticarsi quando si immette un percorso limitato.Accesso a Redux Store dalle route impostate tramite React Router

Finora il mio file routes.js sembra qualcosa di simile:

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

Idealmente, vorrei la mia funzione requireAuth essere un'azione redux che ha accesso al negozio e stato attuale, che funziona in questo modo: store.dispatch(requireAuth()).

Purtroppo non ho accesso al negozio in questo file. Non credo di poter usare davvero lo connect in questo caso per accedere alle azioni pertinenti che voglio. Inoltre, non riesco solo a import store dal file in cui è stato creato il negozio, poiché non è definito al momento del caricamento dell'applicazione.

risposta

53

Il modo più semplice per eseguire questa operazione è passare il negozio a una funzione che restituisce i percorsi (anziché restituire direttamente i percorsi). In questo modo è possibile accedere al negozio in onEnter e altri metodi del router di risposta.

Così, per i vostri itinerari:

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

Poi aggiornare il componente principale per chiamare la funzione getRoutes, passando nel negozio:

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

Per quanto riguarda l'invio un'azione dal requireAuth, si potrebbe scrivere la tua funzione in questo modo:

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(); 
    }); 
}; 

speranza questo aiuta.

+0

questo è un grande esempio. Grazie mille :) – shivam

+1

Come funzionerebbe con il rendering dei server? – Enkay

+0

TY un po ', questo approccio è davvero facile da attuare, ma ho bisogno di chiedere, c'è qualche problema con questo? – StormRage

12

Se si vuole che si potrebbe scrivere route.js come questo:

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

Ho installato un esempio che si potrebbe giocare con in questo codepen.

Non sono sicuro che l'attivazione di un'azione per gestire l'autenticazione sia una buona idea. Personalmente preferisco movimentazione autenticazione in modo diverso:

Invece di utilizzare un gancio onEnter, io uso una funzione di avvolgimento. Voglio proteggere la sezione di amministrazione del mio blog, quindi ho avvolto il componente AdminContainer nei percorsi con una funzione, requireAuthentication, vedi sotto.

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 è una funzione che

  • se l'utente è autenticato, rende il componente avvolto,
  • reindirizza altrimenti Login

Si può vedere qui sotto:

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

Inoltre, requireAuthentication proteggerà tutti i percorsi sotto /admin. E puoi riutilizzarlo dove vuoi.

+0

Sembra AuthenticatedComponent è l'uso del componente visuale React che per scopi di controllo dell'autenticità delle rotte non visive. Non pensi che tutti quei componentiWillMount non riguardino il controllo dell'autenticazione della rotta? –

+0

Sono d'accordo @ alex_1948511, è un hack. Ma poi di nuovo, poche cose sono ben definite nel mondo JS (o forse è solo la mia visione di n00b nella programmazione JS). Sono aperto a qualsiasi suggerimento su come farlo meglio in React. Non ho esaminato questo nei mesi passati, poiché ho trovato questo approccio da qualche parte su Internet, non ho davvero guardato oltre. :-) –

+0

Posso aggiungere, nel router v4, non è possibile nidificare i tag del router. Questo genererà un errore –

2

I lotti sono cambiati nel tempo. onEnter non esiste più sul react-router-4

Il seguente è dal mio vero e proprio progetto per il vostro riferimento

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

Problemi correlati