Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Routes with Redirects in them render twice #55

Open
AustP opened this issue Aug 15, 2017 · 13 comments
Open

Routes with Redirects in them render twice #55

AustP opened this issue Aug 15, 2017 · 13 comments

Comments

@AustP
Copy link

AustP commented Aug 15, 2017

I have a component that checks user authorization and redirects them if they are unauthorized. It redirects them using the Redirect component from react-router-dom.

When I wrapped my routes in a RouteTransition component and passed location and location.key to the Switch component, I noticed that whenever my app would redirect someone, the render logic would execute twice.

If you spin up a server with the following code and visit /dashboard, you will see that "wot" gets logged to the console twice before being redirected to /.


import React from 'react';
import {render} from 'react-dom';
import {BrowserRouter, Redirect, Route, Switch} from 'react-router-dom';
import {RouteTransition} from 'react-router-transition';

render(
  <BrowserRouter>
    <Route render={({location}) => (
      <RouteTransition
        atActive={{opacity: 1}}
        atEnter={{opacity: 0}}
        atLeave={{opacity: 0}}
        pathname={location.pathname}
      >
        <Switch key={location.key} location={location}>
          <Route exact path="/" render={() => <div>Please log in</div>}/>
          <Route exact path="/dashboard" render={() => {
            console.log('wot');
            return <Redirect to="/"/>;
          }}/>
        </Switch>
      </RouteTransition>
    )}/>
  </BrowserRouter>,
  document.getElementById('root')
);

What do I need to do to make my render logic only execute once?

@jacobangel
Copy link
Collaborator

jacobangel commented Aug 16, 2017

Additional evidence for #54

Closing so that we can concentrate the bug tracking.

@AustP
Copy link
Author

AustP commented Aug 18, 2017

@jacobangel @maisano

#54 is now closed but I don't see how I can apply the fixes in that issue to this one. Any ideas?

@maisano
Copy link
Owner

maisano commented Aug 19, 2017

@AustP i should have some time to look at this over the next couple days.

@maisano maisano reopened this Aug 19, 2017
@maisano
Copy link
Owner

maisano commented Aug 25, 2017

@AustP i haven't had the chance to try to repro this yet, but i imagine that the problem is from using <Redirect /> within <RouteTransition />.

my thinking this is because <RouteTransition /> hangs on to a reference of the previously matched component(s) so it can render them in the unmounting transition. my guess is that it's essentially trying to re-render that <Redirect /> from the "/dashboard" route a bunch while unmounting.

if my hunch is true, removing the <Redirect /> in favor of react-router's imperative history api should help. something like the following:

export default class Dashboard extends Component {
  // this probably belongs in some hoc or something
  componentDidMount () {
    if (!isAuthenticated) {
      this.props.history.push('/');
    }
  }

  render() {
    return isAuthenticated
      ? <div>Hi hello</div>
      : null;
  }
}
<Switch key={location.key} location={location}>
  <Route exact path="/" render={() => <div>Please log in</div>}/>
  <Route exact path="/dashboard" component={Dashboard} />
</Switch>

if you could, give that a shot and let me know if it works.

@maisano
Copy link
Owner

maisano commented Sep 1, 2017

@AustP not sure if you got this resolved or not. that said, I just published a new version of this library last night which greatly simplifies the API. it might be worth looking into?

@switchnollie
Copy link

@maisano
I use the new version of the library and like it a lot. Unfortunately, the described error with multiple renders using still occurs with the component and my application heavily relies on this use of .
Can you provide a workaround in the meantime to use Redirect inside AnimatedSwitch?

@Plorark
Copy link

Plorark commented May 8, 2018

Same here. Trying to Redirect programatically but due to the double render it starts a loop that crashes the app.

@maisano
Copy link
Owner

maisano commented May 8, 2018

@Plorark can you paste what the code looks like (specifically the AnimatedSwitch and Redirect instances)?

@Plorark
Copy link

Plorark commented May 8, 2018

@maisano Something like this in switch declaration:

<AnimatedSwitch atEnter={{ opacity: 0 }} atLeave={{ opacity: 0 }} atActive={{ opacity: 1 }} className={style.switchWrapper}>
    <Route exact path="/a/a" component={A} />
    <Route exact path="/a/b" component={B} />
</AnimatedSwitch>

And like this in component A or B:

class A extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            validated: false,
        }
        this.handleValidation = this.handleValidation.bind(this);
    }
    handleValidation() {
        this.setState({validated: true});
    }
    render() {
        if (this.state.validated === true) <Redirect to="/a/b" />
        return (
            <button onClick={this.handleValidation}>LOL</div>
        )
    }
}

And then I just change the state.validated to true to trigger the Redirect.

The error I get are thousands of:
Hash history cannot PUSH the same path; a new entry will not be added to the history stack...
Until the app crash.

It also happens with:
if (this.state.validated === true) history.push('/a/b')
or similars.

@Plorark
Copy link

Plorark commented Jun 18, 2018

So......

@bansawbanchee
Copy link

bansawbanchee commented Jul 7, 2018

Instantiate your own history object and pass it to Router.

NOTE: you cannot use BrowserRouter you will need to use Router.

// history.js
import createHistory from 'history/createBrowserHistory';

export default createHistory();
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route } from 'react-router-dom';
import App from './App';
import history from './history';

ReactDOM.render(
        <Router history={history}>
          <Route path="/" component={App} />
        </Router>
    , document.getElementById('root'));

Now you can use history.push() anywhere you want and new entries will be added to the history stack. You no longer need to pass the location prop around to gain access to history.

// test.js
import history from './history';

const pushItRealGood = () => history.push('/home');

You can even use it in your Redux actionCreators if you like

// actions.js
import history from './history';

export const forgotPassword = data => (dispatch, getState, api) =>
 api.post('/request-password-reset', data)
  .then(() => {
    dispatch({ type: "SOMETHING_HERE", payload });
    history.push('/forgot-password?submission=success');
  }).catch(error => error);

export const resetPassword = (data, token) => (dispatch, getState, api) => 
 api.post(`/users/reset-password?access_token=${token}`, data)
  .then(() => {
    dispatch({ type: "SOMETHING_HERE", payload });
    history.push({ 
       pathname: '/signin',
          state: { 
             message: 'Successfully updated your password. Please login to continue.'
          } 
    });
  }).catch(error => error);

@narkai
Copy link

narkai commented Aug 22, 2018

This code generates a warning.js?d96e:36 Warning: You tried to redirect to the same route warning:

<AnimatedSwitch
  atEnter={{ opacity: 0 }}
  atLeave={{ opacity: 0 }}
  atActive={{ opacity: 1 }}
   mapStyles={(styles) => ({
     opacity: `${styles.opacity}`
   })}
   className='route-wrapper'
>
  <Route path="/hello" render={(props) => <Hello id='hello' {...props}/>} />
  <Route path="/world" render={(props) => <World id='world' {...props}/>} />
  <Redirect to='/hello'/>
</AnimatedSwitch>

@tonydtran
Copy link

I have the same warning as @Lapico. Any news about it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants