Je voudrais utiliser le gestionnaire onEnter de react-router afin d'inviter les utilisateurs à s'authentifier lorsqu'ils entrent dans 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, j'aimerais 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 {{ X0}} 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.

38
robinnnnn 7 mars 2016 à 20:20

4 réponses

Meilleure réponse

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 à partir de 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.

56
Ashley 'CptLemming' Wilson 7 mars 2016 à 19:17

Beaucoup de choses ont changé au fil du temps. onEnter n'existe plus le 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>
  )
}
4
catsky 12 juil. 2017 à 01:54

Après avoir essayé quelques-unes des suggestions ci-dessus, j'ai trouvé que la meilleure façon de suivre l'état de votre magasin avec les mises à jour est d'utiliser la fonction useSelector de React-Redux qui connecte essentiellement un composant fonctionnel au magasin.


function handleRedirect() {
    if(!getToken()) {
        return <Redirect to="/login"/>;
    }
}

const restricted = (Component: _ComponentType, isLoggedIn: boolean) => {
   // Don't redirect here if there is a token in localStorage.
   // This is happening when we are on a restricted route and the user
   // refreshes & the isLoggedIn state hasn't been updated yet.
    return !isLoggedIn ? (
        () => handleRedirect()
    ) : () => <Route component={Component}/>
};

const AuthenticateRoutes = () => {
    const isLoggedIn = useSelector((state: IApplicationState) => state.auth.isLoggedIn);
    return (
        <Switch>
            <Route path="/login" component={Login} />
            <Route path="/downloads" render={restricted(Download, isLoggedIn)} />
        </Switch>
    );
};

export function App() {
    return (
        <Provider store={store}>
            <>
                <Router history={createBrowserHistory()}>
                    <AuthenticateRoutes />
                </Router>
            </>
        </Provider>
    );
}
0
Joe Gasewicz 12 déc. 2019 à 12:00

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 pouvez 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'authentification d'une manière 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

  • si l'utilisateur est authentifié, rend le composant encapsulé,
  • sinon redirige vers 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.

12
Antoni4 26 mai 2016 à 20:20