2016-03-07 9 views
34

Chciałbym skorzystać z obsługi routera reagowania routera onEnter, aby zachęcić użytkowników do uwierzytelnienia podczas wchodzenia na ograniczoną trasę.Uzyskiwanie dostępu do Redux Store z tras skonfigurowanych za pomocą routera React

tej pory mój plik routes.js wygląda mniej więcej tak:

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

Idealnie, chciałbym moje requireAuth funkcję być działanie Redux, że ma dostęp do sklepu i stanu obecnego, który działa w ten sposób: store.dispatch(requireAuth()).

Niestety nie mam dostępu do sklepu w tym pliku. Nie sądzę, żebym mógł użyć naprawdę użyć connect w tym przypadku, aby uzyskać dostęp do odpowiednich akcji, które chcę. Nie mogę też po prostu import store z pliku, w którym jest utworzony sklep, ponieważ jest to niezdefiniowane, gdy aplikacja ładuje się po raz pierwszy.

Odpowiedz

53

Najprostszym sposobem osiągnięcia tego celu jest przekazanie swojego sklepu do funkcji, która zwraca trasy (zamiast zwracać bezpośrednio trasy). W ten sposób można uzyskać dostęp do sklepu w onEnter i innych metodach routera reagowania.

Więc dla swoich tras:

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

Następnie zaktualizować swój główny składnik, aby wywołać funkcję getRoutes, przechodząc w sklepie:

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

As za dysponowanie operację z requireAuth, można napisać Twoja funkcja wygląda następująco:

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

Hope to pomaga.

+0

ów wielki przykład. Wielkie dzięki :) – shivam

+1

Jak by to działało z renderowaniem serwera? – Enkay

+0

TY trochę, takie podejście jest naprawdę łatwe, ale muszę zapytać, czy jest jakaś remisja? – StormRage

12

Jeśli chcesz, że można napisać route.js tak:

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

mam setup przykładem, który można grać w tym codepen.

Nie jestem pewien, czy wyzwalanie akcji w celu obsłużenia autoryzacji jest dobrym pomysłem. Osobiście wolę posługiwać się auth w w inny sposób:

Zamiast używać haczyka onEnter, używam funkcji owijania. Chcę, aby sekcja administratora mojego bloga była chroniona, dlatego zawinąłem komponent AdminContainer na trasach z funkcją, requireAuthentication, patrz poniżej.

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 to funkcja

  • jeśli użytkownik jest uwierzytelniony, renderuje owinięte komponentu
  • inaczej przekierowuje do Login

Można go zobaczyć poniżej:

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

Ponadto, requireAuthentication będzie chronić wszystkie trasy pod numerem /admin. I możesz go użyć ponownie, gdziekolwiek chcesz.

+0

Wygląda jak AuthenticatedComponent jest użycie wizualnego komponentu React, który służy do niezauważalnego sprawdzania autentyczności trasy. Nie sądzisz, że wszystkie te elementy componentWillMount nie dotyczą w ogóle sprawdzania autentyczności trasy? –

+0

Zgadzam się @ alex_1948511, to hack. Ale znowu, bardzo niewiele rzeczy jest dobrze zdefiniowanych w świecie JS (a może to po prostu mój pogląd jako n00b w programowaniu JS). Jestem otwarty na wszelkie sugestie, jak to zrobić lepiej w React. Nie analizowałem tego w ciągu ostatnich miesięcy, ponieważ znalazłem takie podejście gdzieś w Internecie, tak naprawdę nie wyglądałem dalej. :-) –

+0

Mogę dodać, że w Routerie v4 nie można zagnieżdżać znaczników routera. Spowoduje to błąd –

2

Wiele zmieniło się z czasem. onEnter już nie istnieje na react-router-4

Poniżej od mojego prawdziwego projektu celach

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

Powiązane problemy