Je voudrais utiliser le gestionnaire onEnter
de react-router afin d'inviter les utilisateurs à s'authentifier lors de la saisie d'un itinéraire restreint.
Jusqu'à présent, mon fichier routes.js
Ressemble à ceci:
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>
)
Idéalement, je voudrais que ma fonction requireAuth
soit une action redux qui a accès au magasin et à l'état actuel, qui fonctionne comme ceci: store.dispatch(requireAuth())
.
Malheureusement, je n'ai pas accès au magasin dans ce fichier. Je ne pense pas pouvoir vraiment utiliser connect
dans ce cas pour accéder aux actions pertinentes que je souhaite. Je ne peux pas non plus simplement import store
À partir du fichier où le magasin est créé, car cela n'est pas défini lors du premier chargement de l'application.
La façon la plus simple d'y parvenir est de passer votre boutique à une fonction qui renvoie vos itinéraires (plutôt que de renvoyer vos itinéraires directement). De cette façon, vous pouvez accéder au magasin dans onEnter
et à d'autres méthodes de routeur de réaction.
Donc pour vos itinéraires:
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>
);
)
Mettez ensuite à jour votre composant principal pour appeler la fonction getRoutes
, en passant dans le magasin:
<Provider store={ store }>
<Router history={ history }>
{ getRoutes(store) }
</Router>
</Provider>
Quant à l'envoi d'une action depuis requireAuth
, vous pouvez écrire votre fonction comme ceci:
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();
});
};
J'espère que cela t'aides.
Si vous voulez que vous puissiez écrire route.js comme ceci:
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>
);
);
J'ai configuré un exemple avec lequel vous pourriez jouer dans ce codepen .
Je ne sais pas si le déclenchement d'une action pour gérer l'authentification est une bonne idée. Personnellement, je préfère gérer l'auth de façon différente :
Au lieu d'utiliser un crochet onEnter
, j'utilise une fonction d'habillage. Je veux que la section admin de mon blog soit protégée, j'ai donc encapsulé le composant AdminContainer
dans les routes avec une fonction, requireAuthentication
, voir ci-dessous.
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
est une fonction qui
Login
Vous pouvez le voir ci-dessous:
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);
}
De plus, requireAuthentication
protégera toutes les routes sous /admin
. Et vous pouvez le réutiliser où vous le souhaitez.
Beaucoup de choses ont changé au fil du temps. onEnter
n'existe plus sur react-router-4
Ce qui suit est de mon vrai projet pour votre référence
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>
)
}