Comment utiliser Redirect dans le nouveau react-router-dom de Reactjs


132

J'utilise la dernière version du module react-router, nommé react-router-dom, qui est devenu la valeur par défaut lors du développement d'applications Web avec React. Je souhaite savoir comment effectuer une redirection après une requête POST. J'ai créé ce code, mais après la demande, rien ne se passe. Je passe en revue sur le Web, mais toutes les données concernent les versions précédentes du routeur React, et non avec la dernière mise à jour.

Code:

import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Redirect } from 'react-router'

import SignUpForm from '../../register/components/SignUpForm';
import styles from './PagesStyles.css';
import axios from 'axios';
import Footer from '../../shared/components/Footer';

class SignUpPage extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      errors: {},
      client: {
        userclient: '',
        clientname: '',
        clientbusinessname: '',
        password: '',
        confirmPassword: ''
      }
    };

    this.processForm = this.processForm.bind(this);
    this.changeClient = this.changeClient.bind(this);
  }

  changeClient(event) {
    const field = event.target.name;
    const client = this.state.client;
    client[field] = event.target.value;

    this.setState({
      client
    });
  }

  async processForm(event) {
    event.preventDefault();

    const userclient = this.state.client.userclient;
    const clientname = this.state.client.clientname;
    const clientbusinessname = this.state.client.clientbusinessname;
    const password = this.state.client.password;
    const confirmPassword = this.state.client.confirmPassword;
    const formData = { userclient, clientname, clientbusinessname, password, confirmPassword };

    axios.post('/signup', formData, { headers: {'Accept': 'application/json'} })
      .then((response) => {
        this.setState({
          errors: {}
        });

        <Redirect to="/"/> // Here, nothings happens
      }).catch((error) => {
        const errors = error.response.data.errors ? error.response.data.errors : {};
        errors.summary = error.response.data.message;

        this.setState({
          errors
        });
      });
  }

  render() {
    return (
      <div className={styles.section}>
        <div className={styles.container}>
          <img src={require('./images/lisa_principal_bg.png')} className={styles.fullImageBackground} />
          <SignUpForm 
            onSubmit={this.processForm}
            onChange={this.changeClient}
            errors={this.state.errors}
            client={this.state.client}
          />
          <Footer />
        </div>
      </div>
    );
  }
}

export default SignUpPage;

1
Votre Redirectressemble à JSX, pas JS.
elmeister

pouvez-vous vous fournir le code du composant complet
KornholioBeavis

Oui, j'utilise JSX. Eh bien, j'ai peut-être besoin de clarifier. La requête POST est à l'intérieur d'un composant REACT qui effectue la requête.
maoooricio

@KornholioBeavis, bien sûr, maintenant vous pouvez voir complet. Je crée le serveur avec expressjs, je ne sais pas si vous avez besoin de ces données
maoooricio

Pouvez-vous valider que vous recevez une réponse de rappel de axios.post? Aussi pourquoi utilisez-vous la fonction asynchrone sans attendre nulle part?
KornholioBeavis

Réponses:


198

Vous devez utiliser setStatepour définir une propriété qui rendra l' <Redirect>intérieur de votre render()méthode.

Par exemple

class MyComponent extends React.Component {
  state = {
    redirect: false
  }

  handleSubmit () {
    axios.post(/**/)
      .then(() => this.setState({ redirect: true }));
  }

  render () {
    const { redirect } = this.state;

     if (redirect) {
       return <Redirect to='/somewhere'/>;
     }

     return <RenderYourForm/>;
}

Vous pouvez également voir un exemple dans la documentation officielle: https://reacttraining.com/react-router/web/example/auth-workflow


Cela dit, je vous suggère de mettre l'appel API dans un service ou quelque chose. Ensuite, vous pouvez simplement utiliser l' historyobjet pour router par programme. C'est ainsi que fonctionne l' intégration avec redux .

Mais je suppose que vous avez vos raisons de le faire de cette façon.


1
@sebastian Sebald qu'entendez-vous par: put the API call inside a service or something?
andrea-f

1
Avoir un tel appel d'API (asynchrone) dans votre composant rendra plus difficile le test et la réutilisation. Il est généralement préférable de créer un service puis de l'utiliser (par exemple) dans componentDidMount. Ou mieux encore, créez un HOC qui «enveloppe» votre API.
Sebastian Sebald

6
Faites attention que vous devez inclure Redirect pour l'utiliser en début de fichier: import {Redirect} de 'react-router-dom'
Alex

3
Oui, sous le capot Redirectappelle history.replace. Si vous souhaitez accéder à l' historyobjet, utilisez withRoutet/ Route.
Sebastian Sebald

1
react-router> = 5.1 inclut maintenant des crochets, vous pouvez donc simplementconst history = useHistory(); history.push("/myRoute")
TheDarkIn1978

34

Voici un petit exemple en réponse au titre car tous les exemples mentionnés sont compliqués à mon avis ainsi que celui officiel.

Vous devez savoir comment transpiler es2015 et rendre votre serveur capable de gérer la redirection. Voici un extrait pour express. Plus d'informations à ce sujet peuvent être trouvées ici .

Assurez-vous de mettre ceci sous toutes les autres routes.

const app = express();
app.use(express.static('distApp'));

/**
 * Enable routing with React.
 */
app.get('*', (req, res) => {
  res.sendFile(path.resolve('distApp', 'index.html'));
});

Il s'agit du fichier .jsx. Remarquez comment le chemin le plus long vient en premier et devenez plus général. Pour les itinéraires les plus généraux, utilisez l'attribut exact.

// Relative imports
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';

// Absolute imports
import YourReactComp from './YourReactComp.jsx';

const root = document.getElementById('root');

const MainPage= () => (
  <div>Main Page</div>
);

const EditPage= () => (
  <div>Edit Page</div>
);

const NoMatch = () => (
  <p>No Match</p>
);

const RoutedApp = () => (
  <BrowserRouter >
    <Switch>
      <Route path="/items/:id" component={EditPage} />
      <Route exact path="/items" component={MainPage} />          
      <Route path="/yourReactComp" component={YourReactComp} />
      <Route exact path="/" render={() => (<Redirect to="/items" />)} />          
      <Route path="*" component={NoMatch} />          
    </Switch>
  </BrowserRouter>
);

ReactDOM.render(<RoutedApp />, root); 

1
cela ne fonctionne pas tout le temps. si vous avez une redirection de home/hello> home/hello/1mais allez à home/helloet appuyez sur Entrée, elle ne redirigera pas la première fois. des idées pourquoi ??
The Walrus

Je vous conseille d'utiliser si possible "create-react-app" et de suivre la documentation de react-router. Avec "create-react-app", tout fonctionne bien pour moi. Je n'ai pas pu adapter ma propre application react au nouveau react-router.
Matthis Kohli


8

React Router v5 vous permet désormais de rediriger simplement en utilisant history.push () grâce au hook useHistory () :

import { useHistory } from "react-router"

function HomeButton() {
  let history = useHistory()

  function handleClick() {
    history.push("/home")
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  )
}

6

Essayez quelque chose comme ça.

import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Redirect } from 'react-router'

import SignUpForm from '../../register/components/SignUpForm';
import styles from './PagesStyles.css';
import axios from 'axios';
import Footer from '../../shared/components/Footer';

class SignUpPage extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      errors: {},
      callbackResponse: null,
      client: {
        userclient: '',
        clientname: '',
        clientbusinessname: '',
        password: '',
        confirmPassword: ''
      }
    };

    this.processForm = this.processForm.bind(this);
    this.changeClient = this.changeClient.bind(this);
  }

  changeClient(event) {
    const field = event.target.name;
    const client = this.state.client;
    client[field] = event.target.value;

    this.setState({
      client
    });
  }

  processForm(event) {
    event.preventDefault();

    const userclient = this.state.client.userclient;
    const clientname = this.state.client.clientname;
    const clientbusinessname = this.state.client.clientbusinessname;
    const password = this.state.client.password;
    const confirmPassword = this.state.client.confirmPassword;
    const formData = { userclient, clientname, clientbusinessname, password, confirmPassword };

    axios.post('/signup', formData, { headers: {'Accept': 'application/json'} })
      .then((response) => {
        this.setState({
          callbackResponse: {response.data},
        });
      }).catch((error) => {
        const errors = error.response.data.errors ? error.response.data.errors : {};
        errors.summary = error.response.data.message;

        this.setState({
          errors
        });
      });
  }

const renderMe = ()=>{
return(
this.state.callbackResponse
?  <SignUpForm 
            onSubmit={this.processForm}
            onChange={this.changeClient}
            errors={this.state.errors}
            client={this.state.client}
          />
: <Redirect to="/"/>
)}

  render() {
    return (
      <div className={styles.section}>
        <div className={styles.container}>
          <img src={require('./images/lisa_principal_bg.png')} className={styles.fullImageBackground} />
         {renderMe()}
          <Footer />
        </div>
      </div>
    );
  }
}

export default SignUpPage;

Ça marche !, MERCI beaucoup. Ceci est une autre façon de procéder.
maoooricio

Vous ne devriez pas faire de requêtes HTTP dans vos fichiers de composants
Kermit_ice_tea

Pouvez-vous partager ce qui se trouve à l'intérieur de l'importation SignUpForm depuis '../../register/components/SignUpForm' ;? J'essaie d'en tirer des leçons. Bien que dans mon cas, j'utilise un formulaire redux
Temi 'Topsy' Bello

3

Vous pouvez également utiliser withRouter. Vous pouvez accéder aux historypropriétés et les plus proches de l' objet <Route>« s matchvia le withRoutercomposant d'ordre supérieur. withRoutertransmettra les accessoires mis à jour match, locationet historyau composant encapsulé chaque fois qu'il sera rendu.

import React from "react"
import PropTypes from "prop-types"
import { withRouter } from "react-router"

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component {
  static propTypes = {
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  }

  render() {
    const { match, location, history } = this.props

    return <div>You are now at {location.pathname}</div>
  }
}
// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)

Ou juste:

import { withRouter } from 'react-router-dom'

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))

1

vous pouvez écrire un hoc à cet effet et écrire une redirection d'appel de méthode, voici le code:

import React, {useState} from 'react';
import {Redirect} from "react-router-dom";

const RedirectHoc = (WrappedComponent) => () => {
    const [routName, setRoutName] = useState("");
    const redirect = (to) => {
        setRoutName(to);
    };


    if (routName) {
        return <Redirect to={"/" + routName}/>
    }
    return (
        <>
            <WrappedComponent redirect={redirect}/>
        </>
    );
};

export default RedirectHoc;

1
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-router-dom": "^4.2.2"

Pour accéder à une autre page (page À propos dans mon cas), j'ai installé prop-types. Ensuite, je l'importe dans le composant correspondant.Et je l'ai this.context.router.history.push('/about')utilisé.Et il est parcouru.

Mon code est,

import React, { Component } from 'react';
import '../assets/mystyle.css';
import { Redirect } from 'react-router';
import PropTypes from 'prop-types';

export default class Header extends Component {   
    viewAbout() {
       this.context.router.history.push('/about')
    }
    render() {
        return (
            <header className="App-header">
                <div className="myapp_menu">
                    <input type="button" value="Home" />
                    <input type="button" value="Services" />
                    <input type="button" value="Contact" />
                    <input type="button" value="About" onClick={() => { this.viewAbout() }} />
                </div>
            </header>
        )
    }
}
Header.contextTypes = {
    router: PropTypes.object
  };

0

Pour accéder à un autre composant, vous pouvez utiliser this.props.history.push('/main');

import React, { Component, Fragment } from 'react'

class Example extends Component {

  redirect() {
    this.props.history.push('/main')
  }

  render() {
    return (
      <Fragment>
        {this.redirect()}
      </Fragment>
    );
   }
 }

 export default Example

1
React lance un avertissement: Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
Robotron

0

La solution la plus simple pour naviguer vers un autre composant est (Exemple navigue vers le composant de messagerie en cliquant sur l'icône):

<MailIcon 
  onClick={ () => { this.props.history.push('/mails') } }
/>

0

Vous pouvez également utiliser le rendu conditionnel React.

import { Redirect } from "react-router";
import React, { Component } from 'react';

class UserSignup extends Component {
  constructor(props) {
    super(props);
    this.state = {
      redirect: false
    }
  }
render() {
 <React.Fragment>
   { this.state.redirect && <Redirect to="/signin" /> }   // you will be redirected to signin route
}
</React.Fragment>
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.