Como usar o Redirect no novo react-router-dom do Reactjs

131

Estou usando a última versão do módulo react-router, denominada react-router-dom, que se tornou o padrão ao desenvolver aplicativos da Web com o React. Quero saber como fazer um redirecionamento após uma solicitação POST. Eu tenho feito esse código, mas após a solicitação, nada acontece. Eu analiso na web, mas todos os dados são sobre versões anteriores do roteador de reação e não com a última atualização.

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
fonte
1
Você Redirectparece JSX, não JS.
Elmeister
você pode fornecer-lhe todo o código do componente
KornholioBeavis
Sim, estou usando JSX. Bem, talvez eu precise esclarecer. A solicitação POST está dentro de um componente REACT que faz a solicitação.
Maoooricio 5/05
@KornholioBeavis, claro, agora você pode ver a íntegra. Eu faço o servidor com expressjs, eu não sei se você precisa destes dados
maoooricio
Você pode validar que está recebendo uma resposta de retorno de chamada do axios.post? Além disso, por que você está usando a função assíncrona sem aguardar em qualquer lugar?
KornholioBeavis

Respostas:

197

Você precisa usar setStatepara definir uma propriedade que renderizará o <Redirect>interior do seu render()método.

Por exemplo

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

Você também pode ver um exemplo na documentação oficial: https://reacttraining.com/react-router/web/example/auth-workflow


Dito isso, sugiro que você coloque a chamada da API em um serviço ou algo assim. Então você pode simplesmente usar o historyobjeto para rotear programaticamente. É assim que a integração com o redux funciona.

Mas acho que você tem seus motivos para fazê-lo dessa maneira.

Sebastian Sebald
fonte
1
@ sebastian sebald o que você quer dizer com put the API call inside a service or something:?
andrea-f
1
Ter uma chamada de API (assíncrona) dentro do seu componente dificultará o teste e a reutilização. Geralmente, é melhor criar um serviço e usá-lo (por exemplo) no componentDidMount. Ou melhor ainda, crie um HOC que "envolva" sua API.
Sebastian Sebald
6
Preste atenção que você deve incluir Redirecionar para usá-lo no início do arquivo: import {Redirect} de 'reagem-router-dom'
Alex
3
Sim, sob o capô Redirectestá chamando history.replace. Se você deseja acessar o historyobjeto, use withRoutet/ Route.
Sebastian Sebald 25/03
1
react-router> = 5.1 inclui agora ganchos, então você pode apenasconst history = useHistory(); history.push("/myRoute")
TheDarkIn1978
34

Aqui, um pequeno exemplo de resposta ao título, como todos os exemplos mencionados, é complicado na minha opinião e também o oficial.

Você deve saber como transpilar o es2015 e tornar seu servidor capaz de lidar com o redirecionamento. Aqui está um trecho para expressar. Mais informações relacionadas a isso podem ser encontradas aqui .

Coloque isso abaixo de todas as outras rotas.

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

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

Este é o arquivo .jsx. Observe como o caminho mais longo vem primeiro e fica mais geral. Para as rotas mais gerais, use o atributo exato.

// 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
fonte
1
isso não funciona o tempo todo. se você tiver um redirecionamento de home/hello>, home/hello/1mas vá para home/helloe pressione Enter, ele não será redirecionado pela primeira vez. alguma idéia por que ??
The Walrus
Aconselho que você use "create-react-app", se possível, e siga a documentação do react-router. Com "create-react-app" tudo funciona bem para mim. Não consegui adaptar meu próprio aplicativo de reação ao novo roteador de reação.
Matthis Kohli
8

O React Router v5 agora permite que você simplesmente redirecione usando history.push () graças ao 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
fonte
6

Tente algo assim.

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
fonte
Funciona !, MUITO OBRIGADO. Essa é outra maneira de fazer isso.
Maoooricio 7/04
Você não deve estar fazendo solicitações HTTP em seus arquivos de componente #
Kermit_ice_tea
Você pode compartilhar o que há dentro do Import SignUpForm de '../../register/components/SignUpForm' ;? Eu estou tentando aprender com isso. Embora, no meu caso, eu esteja usando um formulário redux
Temi 'Topsy' Bello
3

Como alternativa, você pode usar withRouter. Você pode obter acesso ao historypropriedades do objeto e os mais próximos <Route>'s matchatravés da withRoutercomponente de ordem superior. withRouterpassará atualizado match, locatione historyprops para o componente agrupado sempre que renderizado.

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

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

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

você pode escrever um hoc para esse fim e escrever um redirecionamento de chamada de método, eis o 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
fonte
1
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-router-dom": "^4.2.2"

Para navegar para outra página (página Sobre, no meu caso), instalei prop-types. Eu o importo no componente correspondente e useithis.context.router.history.push('/about') . E ele é navegado.

Meu código é

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
fonte
0

Para navegar para outro componente, você pode 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
fonte
1
React lança um aviso: 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

A solução mais simples para navegar para outro componente é (o exemplo navega para o componente de e-mails clicando no ícone):

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

Como alternativa, você pode usar Reagir renderização condicional.

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
fonte