Come usare Redirect nel nuovo react-router-dom di Reactjs


132

Sto usando l'ultima versione del modulo React-Router, chiamata React-Router-Dom, che è diventata l'impostazione predefinita durante lo sviluppo di applicazioni web con React. Voglio sapere come fare un reindirizzamento dopo una richiesta POST. Ho creato questo codice, ma dopo la richiesta non succede nulla. Esamino sul web, ma tutti i dati riguardano le versioni precedenti del router React, e no con l'ultimo aggiornamento.

Codice:

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
Il tuo Redirectsembra JSX, non JS.
elmeister

puoi fornire l'intero codice del componente
KornholioBeavis

Sì, sto usando JSX. Beh, forse ho bisogno di chiarire. La richiesta POST è all'interno di un componente REACT che effettua la richiesta.
maoooricio

@KornholioBeavis, certo, ora puoi vedere completo. Faccio il server con expressjs, non so se hai bisogno di questi dati
maoooricio

Puoi confermare che stai ricevendo una risposta di richiamata da axios.post? Inoltre, perché stai usando la funzione asincrona senza attendere da nessuna parte?
KornholioBeavis

Risposte:


198

Devi usare setState per impostare una proprietà che renderà l' <Redirect>interno del tuo render()metodo.

Per esempio

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

Puoi anche vedere un esempio nella documentazione ufficiale: https://reacttraining.com/react-router/web/example/auth-workflow


Detto questo, ti suggerirei di inserire la chiamata API all'interno di un servizio o qualcosa del genere. Quindi potresti semplicemente usare il filehistory oggetto per instradare programmaticamente. Ecco come funziona l' integrazione con redux .

Ma immagino che tu abbia le tue ragioni per farlo in questo modo.


1
Sebald @sebastian ciò che si intende per: put the API call inside a service or something?
andrea-f

1
Avere una tale chiamata API (asincrona) all'interno del tuo componente renderà più difficile il test e il riutilizzo. Di solito è meglio creare un servizio e poi usarlo (ad esempio) in componentDidMount. O ancora meglio, crea un HOC che "avvolge" la tua API.
Sebastian Sebald

6
Fai attenzione che devi includere Redirect per usarlo all'inizio del file: import {Redirect} da 'react-router-dom'
Alex

3
Sì, sotto il cofano Redirectsta chiamando history.replace. Se vuoi accedere historyall'oggetto, usa withRoutet/ Route.
Sebastian Sebald

1
react-router> = 5.1 ora include gli hook, quindi puoi semplicementeconst history = useHistory(); history.push("/myRoute")
TheDarkIn1978

34

Ecco un piccolo esempio in risposta al titolo in quanto tutti gli esempi citati sono a mio avviso complicati così come quello ufficiale.

Dovresti sapere come transpile es2015 e come rendere il tuo server in grado di gestire il reindirizzamento. Ecco uno snippet per express. Maggiori informazioni relative a questo possono essere trovate qui .

Assicurati di metterlo sotto tutti gli altri percorsi.

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

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

Questo è il file .jsx. Nota come il percorso più lungo viene prima e diventa più generale. Per i percorsi più generali utilizzare l'attributo esatto.

// 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
questo non funziona sempre. se hai un reindirizzamento da home/hello> home/hello/1ma poi vai su home/helloe premi invio non reindirizzerà la prima volta. qualche idea perché ??
The Walrus

Ti consiglio di usare "create-react-app" se possibile e di seguire la documentazione di react-router. Con "create-react-app" tutto funziona bene per me. Non sono stato in grado di adattare la mia applicazione React al nuovo React-Router.
Matthis Kohli


8

React Router v5 ora ti consente di reindirizzare semplicemente usando history.push () grazie all'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

Prova qualcosa di simile.

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;

Funziona!, GRAZIE mille. Questo è un altro modo per farlo.
maoooricio

Non dovresti effettuare richieste HTTP nei file dei componenti
Kermit_ice_tea

Puoi condividere cosa c'è dentro import SignUpForm da '../../register/components/SignUpForm' ;? Sto cercando di imparare da questo. Anche se nel mio caso, sto usando un modulo redux
Temi 'Topsy' Bello

3

In alternativa, puoi usare withRouter. È possibile ottenere l'accesso alla historyproprietà dell'oggetto e la più vicina <Route>s' matchtramite il withRoutercomponente di ordine superiore. withRouterpasserà gli oggetti di scena aggiornati match, locatione historyal componente avvolto ogni volta che viene eseguito il rendering.

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)

O semplicemente:

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

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

1

puoi scrivere un hoc per questo scopo e scrivere un reindirizzamento della chiamata al metodo, ecco il codice:

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"

Per passare a un'altra pagina (pagina Informazioni nel mio caso), ho installato prop-types. Quindi lo importa nel componente corrispondente e ho usatothis.context.router.history.push('/about') e viene navigato.

Il mio codice è,

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

Per passare a un altro componente che puoi utilizzare 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 lancia un avvertimento: 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 soluzione più semplice per passare a un altro componente è (l'esempio passa al componente di posta facendo clic sull'icona):

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

0

In alternativa, puoi usare il rendering condizionale di 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>
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.