Cómo usar Redirect en el nuevo react-router-dom de Reactjs

132

Estoy usando la última versión del módulo react-router, llamado react-router-dom, que se ha convertido en el predeterminado al desarrollar aplicaciones web con React. Quiero saber cómo realizar una redirección después de una solicitud POST. He estado creando este código, pero después de la solicitud, no pasa nada. Reviso en la web, pero todos los datos son sobre versiones anteriores del enrutador react, y no con la última actualización.

Código:

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;
maoooricio
fuente
1
Sus Redirectmiradas como JSX, no JS.
elmeister
¿Puede proporcionarle el código de componente completo
KornholioBeavis
Sí, estoy usando JSX. Bueno, tal vez necesite aclarar. La solicitud POST está dentro de un componente REACT que realiza la solicitud.
maoooricio
@KornholioBeavis, claro, ahora puedes ver completo. Hago el servidor con expressjs, no sé si necesitas estos datos
maoooricio
¿Puede validar que está recibiendo una respuesta de devolución de llamada de axios.post? Además, ¿por qué está utilizando la función asíncrona sin esperar en ninguna parte?
KornholioBeavis

Respuestas:

198

Tienes que usar setStatepara establecer una propiedad que renderice el <Redirect>interior de tu render()método.

P.ej

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

También puede ver un ejemplo en la documentación oficial: https://reacttraining.com/react-router/web/example/auth-workflow


Dicho esto, te sugiero que pongas la llamada API dentro de un servicio o algo. Entonces podría usar el historyobjeto para enrutar programáticamente. Así es como funciona la integración con redux .

Pero supongo que tienes tus razones para hacerlo de esta manera.

Sebastián Sebald
fuente
1
@sebastian sebald ¿a qué te refieres con put the API call inside a service or something:?
andrea-f
1
Tener una llamada a la API (asíncrona) de este tipo dentro de su componente hará que sea más difícil de probar y reutilizar. Por lo general, es mejor crear un servicio y luego usarlo (por ejemplo) en componentDidMount. O incluso mejor, cree un HOC que "envuelva" su API.
Sebastian Sebald
6
Preste atención a que debe incluir Redirect para usarlo al comienzo del archivo: import {Redirect} from 'react-router-dom'
Alex
3
Sí, bajo el capó Redirectestá llamando history.replace. Si desea acceder al historyobjeto, use withRoutet/ Route.
Sebastian Sebald
1
react-router> = 5.1 ahora incluye ganchos, por lo que puede simplementeconst history = useHistory(); history.push("/myRoute")
TheDarkIn1978
34

Aquí un pequeño ejemplo como respuesta al título ya que todos los ejemplos mencionados son complicados en mi opinión al igual que el oficial.

Debe saber cómo transpilar es2015 y hacer que su servidor pueda manejar la redirección. Aquí hay un fragmento de expreso. Puede encontrar más información relacionada con esto aquí .

Asegúrese de poner esto debajo de todas las demás rutas.

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

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

Este es el archivo .jsx. Observe cómo el camino más largo viene primero y se vuelve más general. Para las rutas más generales, utilice el atributo exacto.

// 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); 
Matthis Kohli
fuente
1
esto no funciona todo el tiempo. si tiene una redirección desde home/hello> home/hello/1pero luego vaya a home/helloy presione Intro, no se redirigirá la primera vez. alguna idea por qué?
The Walrus
Le aconsejo que utilice "create-react-app" si es posible y siga la documentación de react-router. Con "create-react-app" todo funciona bien para mí. No pude adaptar mi propia aplicación de reacción al nuevo enrutador de reacción.
Matthis Kohli
8

React Router v5 ahora le permite simplemente redirigir usando history.push () gracias al gancho 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>
  )
}
Thanh-Quy Nguyen
fuente
6

Prueba algo como esto.

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;
KornholioBeavis
fuente
¡Funciona !, MUCHAS GRACIAS. Esta es otra forma de hacer esto.
maoooricio
No debería realizar solicitudes HTTP en sus archivos de componentes
Kermit_ice_tea
¿Puede compartir lo que hay dentro de importar SignUpForm desde '../../register/components/SignUpForm' ;? Estoy tratando de aprender de esto. Aunque en mi caso, estoy usando una forma redux
Temi 'Topsy' Bello
3

Alternativamente, puede usar withRouter. Usted puede obtener acceso a la historypropiedades de los objetos y los más cercanos <Route>'s matcha través del withRoutercomponente de orden superior. withRouterpasará los accesorios actualizados match, locationy historyal componente envuelto siempre que se procese.

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 solo:

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

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))
Ömürcan Cengiz
fuente
1

puede escribir un hoc para este propósito y escribir una redirección de llamada de método, aquí está el código:

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;
zia
fuente
1
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-router-dom": "^4.2.2"

Para navegar a otra página (página Acerca de en mi caso), instalé prop-types. Luego lo importo en el componente correspondiente. this.context.router.history.push('/about')Y lo usé . Y se navega.

Mi código es

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
  };
sojan
fuente
0

Para navegar a otro componente, puede usar 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
Morris S
fuente
1
React lanza una advertencia: 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 solución más sencilla para navegar a otro componente es (el ejemplo navega al componente de correos haciendo clic en el icono):

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

Alternativamente, puede usar la representación condicional de 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>
}
Niyongabo
fuente