STRML / react-router-component

Declarative router component for React.
http://strml.viewdocs.io/react-router-component
MIT License
872 stars 94 forks source link

Redirect. #92

Open chrisdew opened 10 years ago

chrisdew commented 10 years ago

I'm looking for react-router-component's equivalent of https://github.com/rackt/react-router 's <Redirect ...>.

If there isn't one, how do most users of react-router-component currently do their redirects?

STRML commented 10 years ago

We don't have a declarative way to do this currently, the manual way to do this would be to insert a fallback route in a hierarchy and fire a new route based on the current location.

Do you mind if I ask what your use case is?

chrisdew commented 10 years ago

Do you think <redirect ...> should be a feature of react-router-component?

Is there an architectural reason why it hasn't been implemented, or is it a matter of time and priorities?

chrisdew commented 10 years ago

I have an application with several pages which are peers - i.e. /foo, /bar, /baz and /quuz.

I want the root route / to be redirected to /baz so that when we change the default page, user's /baz bookmarks will still work.

If we used BazPage as the handler for /, users' bookmarks would break when we changed the default page.

STRML commented 10 years ago

I see. That makes sense to me. It is just a matter of time and priorities. I think it should be rather simple to create a component that reads the current URL on mount, reaches out to its parent router and reroutes. Would be very happy to accept a PR. On Oct 15, 2014, at 5:37 PM, Chris Dew notifications@github.com wrote:

I have an application with several pages which are peers - i.e. /foo, /bar, /baz and /quuz.

I want the root route / to be directed to /baz so that when we change the default page, user's /baz bookmarks still work.

If we used BazPage as the handler for /, users' bookmarks would break when we changed the default page.

— Reply to this email directly or view it on GitHub.

chrisdew commented 10 years ago

Just posting my interim solution, in case it helps anyone.

function createRedirect(url) {
  return React.createClass({
    mixins: [NavigatableMixin],

    componentDidMount: function () {
      console.log('redirecting to', url);
      return this.navigate(url);
    },

    render: function () {
      return <p>redirecting to <a href={url}>{url}</a></p>;
    }
  });
}

    <Pages className="App" path={this.props.path}>
      <Page path="/" handler={DashboardPage} />
      ...
      <Page path="/commissioning" handler={this.state.logged_in ? CommissioningPage : createRedirect("/login")}/>
      <Page path="/login" handler={LoginPage}/>
      <NotFound handler={NotFoundHandler} />
    </Pages>

Note: This is only "mostly" functional. If for some reason if you request the redirected page directly (i.e. by typing in the URL), then it change the page location, but will display the blue link instead of showing the content of the new page. I have no idea why this is.

jbach commented 9 years ago

@chrisdew Have you found a solution to this particular problem yet? I face the same problem, page location being correct, but showing the wrong component.

MandarinConLaBarba commented 9 years ago

@STRML I've started to work on this but I suspect I'm going about this in the wrong way.

I've gotten the following scenario to work:

/some/path -> /another/path

But when interpolation is involved, the redirect takes the literal path template (not interpolated):

/user/1234 -> /user/:userId/home //where path={"/user/:userId"} and redirectPath={"/user/:userId/home"}

The relevant code here:

https://github.com/MandarinConLaBarba/react-router-component/blob/issue-92-redirect/lib/Route.js#L30-L59

Thoughts?

pospi commented 9 years ago

Not sure if I am abusing navigate(), but I am trying to use this same approach from componentWillMount() in order to create authentication mixins and I have the same issue there with the new route not being rendered in response to the redirect.

jsg2021 commented 9 years ago

In my application this component handles redirects by just <Redirect path={newPath}/>

import React from 'react';
import Router from 'react-router-component';
import Loading from 'common/components/Loading';

export default React.createClass({
    displayName: 'Redirect',
    mixins: [Router.NavigatableMixin],

    propTypes: {
        force: React.PropTypes.bool,
        location: React.PropTypes.string
    },

    performRedirect (props) {
        let loc = props.location;
        let location = global.location;
        let currentFragment = location && location.hash;

        if (props.force) {
            console.debug('Forceful redirect to: %s', loc);
            return location.replace(loc);
        }

        if (loc && loc.indexOf('#') === -1 && currentFragment) {
            loc = loc +
                    (currentFragment.charAt(0) !== '#' ? '#' : '') +
                    currentFragment;
        }

        // let routes = this.context.router.props.children.map(x=>x.props.path || 'default');
        // console.debug('Redirecting to %s, routes: %o', loc, routes);
        console.debug('Redirecting to %s', loc);
        this.navigate(loc, {replace: true});
    },

    startRedirect(p) {
        clearTimeout(this.pendingRedirect);
        this.pendingRedirect = setTimeout(()=> this.performRedirect(p), 1);
    },

    componentDidMount () {
        this.startRedirect(this.props);
    },

    componentWillReceiveProps (props) {
        this.startRedirect(props);
    },

    render () {
        return (<Loading message="Redirecting..."/>);
    }
});
pospi commented 9 years ago

Works for me; I ended up taking your code and turning it into a mixin that wraps NavigatableMixin. The key component is the setTimeout(), so long as you call .navigate() outside of the React render loop the router will re-render the page correctly. Seems like a workaround for a bug within the framework but certainly works well enough for now.

saidimu commented 9 years ago

For a related redirect issue https://github.com/STRML/react-router-component/issues/24#issuecomment-135113492, here's what I use that so far works even when directly typing the url:

https://gist.github.com/saidimu/2758402f7916f4e35dee

// jshint unused:true
var React = require('react');

var NavigatableMixin = require('react-router-component').NavigatableMixin;

var Redirect = React.createClass({
  mixins: [NavigatableMixin],

  _redirect: function(url) {
    this.navigate(url);
  },

  propTypes: {
    url: React.PropTypes.string.isRequired,
  },

  componentDidMount: function() {
    console.log('Redirecting to %s: ', this.props.url);
    this._redirect(this.props.url);
  },

  render: function() {
    return null;
  }
});//Redirect

module.exports = Redirect;

@chrisdew @jbach I wonder if the issue you faced is because of not rendering null?

jsg2021 commented 9 years ago

@saidimu Redirects replace the current route... you should add {replace: true} as the second argument to navigate

saidimu commented 9 years ago

@jsg2021 Where is the replace parameter documented? I see no appreciable difference between omitting it, {replace: true} and {replace:false}

jsg2021 commented 9 years ago

@saidimu It's not documented. Its internal implementation. You are calling parts of the router that were not meant to be exposed. (much like context in React) See this line.

The only difference will be your history in the browser will not include the route the redirect was on...
ex:

if /foo/bar renders a redirect to /foo/baz, when you arrive at /foo/baz hitting the back button will take you to the page before you got to /foo/bar because the redirect replaces that entry in the history.

The Redirect component I shared above is a complete example that is heavily used in my company's application.