web-dev-qa-db-fra.com

React Les routes privées du routeur / la redirection ne fonctionne pas

J'ai légèrement ajusté le React exemple de routeur pour que les routes privées jouent Nice avec Redux, mais aucun composant n'est rendu lors de la liaison ou de la redirection vers d'autres 'pages'. L'exemple se trouve ici:

https://reacttraining.com/react-router/web/example/auth-workflow

Leur composant PrivateRoute ressemble à ceci:

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={props => (
    fakeAuth.isAuthenticated ? (
      <Component {...props}/>
    ) : (
      <Redirect to={{
        pathname: '/login',
        state: { from: props.location }
      }}/>
    )
  )}/>
)

Mais, comme je l’ai incorporé dans une application Redux, j’ai dû ajuster un peu PrivateRoute pour pouvoir accéder au magasin Redux, ainsi qu’à la route Props:

const PrivateRouteComponent = (props) => (
    <Route {...props.routeProps} render={() => (
    props.logged_in ? (
        <div>{props.children}</div>
        ) : (
        <Redirect to={{
            pathname: '/login',
            state: { from: props.location }
        }} /> )
    )} />
);

const mapStateToProps = (state, ownProps) => {
    return {
        logged_in: state.auth.logged_in,
        location: ownProps.path,
        routeProps: {
            exact: ownProps.exact,
            path: ownProps.path
        }
    };
};

const PrivateRoute = connect(mapStateToProps, null)(PrivateRouteComponent);
export default PrivateRoute

Chaque fois que je ne suis pas connecté et que je clique sur un PrivateRoute, je suis correctement redirigé vers/login. Cependant, après, par exemple, vous connecter et utiliser un fichier <Redirect .../>, ou en cliquant sur un <Link ...> sur PrivateRoute, l'URI est mis à jour, mais la vue ne l'est pas. Il reste sur le même composant.

Qu'est-ce que je fais mal?


Juste pour compléter le tableau, dans l'application index.js il y a des éléments non pertinents et les itinéraires sont configurés comme suit:

ReactDOM.render(
    <Provider store={store}>
        <App>
            <Router>
                <div>
                    <PrivateRoute exact path="/"><Home /></PrivateRoute>
                    // ... other private routes
                    <Route path="/login" component={Login} />
                </div>
            </Router>
        </App>
    </Provider>,
    document.getElementById('root')
);
33
Rein

Vous devez envelopper votre Route avec <Switch> tag

ReactDOM.render(
<Provider store={store}>
    <App>
        <Router>
            <div>
                <Switch>
                   <PrivateRoute exact path="/"><Home /></PrivateRoute>
                   // ... other private routes
                   <Route path="/login" component={Login} />
                </Switch>
            </div>
        </Router>
    </App>
</Provider>,
document.getElementById('root'));
11
Eli

Définissez la voie privée pour qu'elle ne soit pas pure:

export default connect(mapStateToProps, null, null, {
  pure: false,
})(PrivateRoute);

Cela laissera le composant re-rendre.

Veuillez consulter: react-router-4-x-privateroute-not-working-after-connection-to-redux .

8
worker11811

Juste le même problème, je l'ai résolu en rendant mon conteneur App redux et en passant isAuthenticated comme accessoire à PrivateRoute

Voilà, j'espère que ça aide

const App = (props) => {
return (
  <Provider store={store}>
    <Router>
      <div>
        <PrivateRoute path="/secured" component={Secured} isAuthenticated={props.isAuthenticated} />
      </div>
    </Router>
  </Provider>
  );
};

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

export default connect(mapStateToProps)(App);

Puis dans mon PrivateRoute

const PrivateRoute = ({ component: Component, isAuthenticated, ...rest}) => (
<Route
  {...rest}
  render={props => (
    isAuthenticated
    ? (
       <Component {...props} />
    )
    : (<Redirect to={{ pathname: '/login', state: { from: props.location} }} />)
  )}
/>
);

export default PrivateRoute;
6
Danijel

J'ai réussi à le faire fonctionner en utilisant le paramètre rest pour accéder aux données de mapStateToProps:

const PrivateRoute = ({component: Component, ...rest}) => {
  const {isAuthenticated} = rest;

  return (
    <Route {...rest} render={props => (
      isAuthenticated ? (
        <Component {...props}/>
      ) : (
        <Redirect to={{
          pathname: '/login',
          state: {from: props.location}
        }}/>
      )
    )}
    />
  );
};

PrivateRoute.propTypes = {
  isAuthenticated: PropTypes.bool.isRequired,
};

function mapStateToProps(state) {
  return {
    isAuthenticated: state.user.isAuthenticated,
  };
}

export default connect(mapStateToProps)(PrivateRoute);
5
Newm

Eh bien, je pense que la réponse à cette question devrait vraiment être plus détaillée, alors me voilà après 4 heures de fouilles.

Lorsque vous encapsulez votre composant avec connect (), React Redux implémente shouldComponentUpdate (si vous recherchez des réponses sur des problèmes de github) et faites une comparaison superficielle sur les accessoires (il passe par chaque touche de ceux-ci objet et vérifiez si les valeurs sont identiques à '==='). En pratique, cela signifie que votre composant est considéré Pure. Il ne changera maintenant que lorsque ses accessoires changeront et seulement ensuite! C'est le message clé ici. Deuxième message clé, React, le routeur utilise le contexte pour transmettre les objets location, match et history du composant Router to Route. . Il n’utilise pas props .

Voyons maintenant ce qui se passe dans la pratique, car même en sachant cela, je trouve cela toujours assez délicat:

  • Cas 1 :

Après la connexion, il y a 3 clés pour vos accessoires: chemin, composant et auth (donné par connect). Ainsi, en fait, votre composant encapsulé NE SERA PAS du tout restitué lors des modifications d'itinéraire car il s'en fiche. Lorsque les itinéraires changent, vos accessoires ne changent pas et il ne sera pas mis à jour.

  • Cas 3 :

Maintenant que vous avez 4 clés pour accéder à vos accessoires après la connexion: chemin, composant, auth et anyprop. L'astuce ici est que anyprop est un objet créé chaque fois que le composant est appelé. Ainsi, chaque fois que votre composant est appelé, cette comparaison est effectuée: {a: 1} === {a: 1}, ce qui (vous pouvez essayer) vous donne la valeur false. Votre composant est donc mis à jour à chaque fois. Notez cependant que votre composant ne se soucie toujours pas de la route, mais de ses enfants.

  • Cas 2 :

Voilà le mystère ici, parce que je suppose que vous appelez cette ligne dans votre fichier App, et il ne devrait y avoir aucune référence à "auth", et vous devriez avoir une erreur (du moins, c'est ce que je préviens). Mon hypothèse est que "auth" dans votre fichier App fait référence à un objet défini ici.

Maintenant que devons-nous faire?

Je vois deux options ici:

  1. Dites à React Redux que votre composant n'est pas pur, cela supprimera l'injection de sCU et votre composant sera maintenant correctement mis à jour.

    connect (mapStateToProps, null, null, {pure: false}) (PrivateRoute)

  2. Utilisez WithRouter (), ce qui aura pour effet d’injecter l’objet emplacement, correspondance et historique à votre composant comme accessoires. Maintenant, je ne connais pas les composants internes, mais je soupçonne React) que le routeur ne mute pas ces objets; ainsi, chaque fois que la route change, ses accessoires changent (sCU renvoie true) ainsi que votre composant. L’effet secondaire de ceci est que votre arbre React est maintenant pollué par de nombreux trucs WithRouter et Route ...

Référence au problème de github: Traitement du blocage des mises à jour

Vous pouvez voir ici que withRouter est conçu comme un correctif, mais pas une solution recommandée. utiliser pure: false n'est pas mentionné, donc je ne sais pas à quel point ce correctif pourrait être bon.

J'ai trouvé une 3ème solution, bien que je ne sache pas si c'est vraiment une meilleure solution qu'avec withRouter, en utilisant un composant d'ordre supérieur. Vous connectez votre composant d'ordre supérieur au magasin Redux, et maintenant votre itinéraire se fout complètement de ce qu'il restitue, le HOC s'en occupe.

import Notlogged from "./Notlogged";    
function Isloggedhoc({ wrap: Component, islogged, ...props }) {
      return islogged ? <Component {...props} /> : <Notlogged {...props} />;
    }

const mapState = (state, ownprops) => ({
      islogged: state.logged,
      ...ownprops
    });

export default connect(mapState)(Isloggedhoc);

dans votre App.js

<Route path="/PrivateRoute" render={props => <Isloadedhoc wrap={Mycomponent} />} />

Vous pouvez même créer une fonction au curry pour la raccourcir un peu:

function isLogged(component) {
  return function(props) {
    return <Isloadedhoc wrap={component} {...props} />;
  };
}

En l'utilisant comme ça:

<Route path="/PrivateRoute" render={isLogged(Mycomponent)} />
4
TheoH

Je me suis également heurté à ce problème et voici ma solution.

Au lieu de transmettre isAuthenticated à chaque composant <PrivateRoute>, il vous suffit d'obtenir isAuthenticated à partir de l'état situé dans <PrivateRoute> lui-même.

import React from 'react';
import {Route, Redirect, withRouter} from 'react-router-dom';
import {connect} from 'react-redux';

// isAuthenticated is passed as prop here
const PrivateRoute = ({component: Component, isAuthenticated , ...rest}) => {
    return <Route
        {...rest}
        render={
            props => {
                return isAuthenticated ?
                    (
                        <Component {...props} />
                    )
                    :
                    (
                        <Redirect
                            to={{
                                pathname: "/login",
                                state: {from: props.location}
                            }}
                        />
                    )
            }
        }
    />
};

const mapStateToProps = state => (
    {
        // isAuthenticated  value is get from here
        isAuthenticated : state.auth.isAuthenticated 
    }
);

export default withRouter(connect(
    mapStateToProps, null, null, {pure: false}
)(PrivateRoute));
3
Hoang Trinh

Selon documentation de react-router vous pouvez simplement envelopper votre fonction connect avec withRouter:

// before
export default connect(mapStateToProps)(Something)

// after
import { withRouter } from 'react-router-dom'
export default withRouter(connect(mapStateToProps)(Something))

Cela a fonctionné pour moi et mon point de vue a commencé à être mis à jour avec les itinéraires dans ce cas.

1
Oleksii Trekhleb

J'ai le même problème, comme @Rein. Dans mon cas, PrivateRoute a presque la même apparence que la version d'origine, mais n'est connecté qu'à Redux et l'a utilisé à la place de fakeAuth dans l'exemple d'origine.

const PrivateRoute = ({ component: Component, auth, ...rest }) => (
  <Route
   {...rest}
   render={props =>
   auth.isAuthenticated
    ? <Component {...props} />
    : <Redirect to={{ pathname: "/login" }} />}
  />
);

 PrivateRoute.propTypes = {
  auth: PropTypes.object.isRequired,
  component: PropTypes.func.isRequired
 }

 const mapStateToProps = (state, ownProps) => {
  return {
     auth: state.auth
  }
};

export default connect(mapStateToProps)(PrivateRoute);

Utilisation et résultat: -

  1. NE FONCTIONNE PAS mais s'attend à ce que fonctionne
    • <PrivateRoute path="/member" component={MemberPage} />
  2. travaillant mais PAS désireux d’être utilisé comme ceci
    • <PrivateRoute path="/member" component={MemberPage} auth={auth} />
  3. travail. JUSTE de travailler, mais PAS désirée du tout. À partir de là, vous comprenez que si vous connectez PrivateRoute d’origine à Redux, vous devez transmettre des accessoires supplémentaires (n’importe quel accessoire) pour que PrivateRoute fonctionne, sinon cela ne fonctionnera pas. N'importe qui, donnez quelques indices sur ce comportement . C'est mon principale préoccupation. en tant que nouvelle question à
    • <PrivateRoute path="/member" component={MemberPage} anyprop={{a:1}} />
0