react-router redirect to a different domain url
Asked Answered
T

6

29

I am using react-router for client side routing. I have a button and when some one clicks the button, I want to redirect the user to a different url. For e.g I want to redirect the user to "http://www.google.com". I used navigation mixin and used this.transitionTo("https://www.google.com"). But when I do this I get this error Invariant Violation: Cannot find a route named "https://www.google.com".

I can use window.location but is that the right way to go?

Tonl answered 28/6, 2015 at 3:5 Comment(0)
M
36

As pointed out in the comments to this answer, default way of solving this would be to use anchor element (the a tag) with href attribute that points at the destination URL that you'd like to route the user to. A button that has appearance of a button but behavior or an anchor is pretty much a web anti-pattern. See more info in this answer: https://mcmap.net/q/480699/-window-location-href-vs-clicking-on-an-anchor.

That said, there certainly is a potential scenario when a web app needs to perform some action and only then redirect the user. In this case, if primary action the user takes is submitting some data or really performing an action, and redirect is more of a side-effect, then the original question is valid.

In this case, why not use location property of window object? It even provides a nice functional method to go to external location. See the ref.

So, if you have a component, say

class Button extends Component {
  render() {
    return (
      <button onClick={this.handleClick.bind(this)} />
    );
  }
}

then add handleClick that would make the component look like

class Button extends Component {
  handleClick() {
    // do something meaningful, Promises, if/else, whatever, and then
    window.location.assign('http://github.com');
  }

  render() {
    return (
      <button onClick={this.handleClick.bind(this)} />
    );
  }
}

No need to import window since it's global. Should work perfectly in any modern browser.

Also, if you have a component that is declared as a function, you may possibly use the effect hook to change location when state changes, like

const Button = () => {
  const [clicked, setClicked] = useState(false);

  useEffect(() => {
    if (clicked) {
      // do something meaningful, Promises, if/else, whatever, and then
      window.location.assign('http://github.com');
    }
  });

  return (
    <button onClick={() => setClicked(true)}></button>
  );
};
Mcfall answered 9/6, 2016 at 23:4 Comment(5)
By the way, make sure it's really necessary to do so. It may cause lots of pain to other programmers when they start working with you one day, or take on your codebase after you have long since left the project. – Mcfall
Nice usecase of useEffect hooks :) – Garbers
-1, even though "it would work" if some cases, this goes against all the premises of the Web! See more explanations here: https://mcmap.net/q/480699/-window-location-href-vs-clicking-on-an-anchor – Isosteric
Updated the answer. Since the question does not specify whether a redirect that happens in response to a button click is primary action or a side-effect, assuming it is primary action, a should be preferred over button. πŸ‘ – Mcfall
Or you still use a link, because you can just prevent the navigation from happening, do the work, then retrigger the link automatically. I've added example code for that to my answer because even in that scenario: don't use a <button>. – Grate
B
7

You don't need react-router for external links, you can use regular link elements (i.e. <a href="..."/>) just fine.

You only need react-router when you have internal navigation (i.e. from component to component) for which the browser's URL bar should make it look like your app is actually switching "real" URLs.

Edit because people seem to think you can't use an <a href="..." if you need to "do work first", an example of doing exactly that:

render() {
  return <a href={settings.externalLocation} onClick={evt => this.leave(evt)}/>
}

async leave(evt) {
  if (this.state.finalized) return;

  evt.preventDefault();

  // Do whatever you need to do, but do it quickly, meaning that if you need to do
  // various things, do them all in parallel instead of running them one by one:
  await Promise.all([
    utils.doAllTheMetrics(),
    user.logOutUser(),
    store.cleanUp(),
    somelib.whatever(),
  ]);

  // done, let's leave.
  this.setState({ finalized: true }), () => evt.target.click());
}

And that's it: when you click the link (that you styled to look like a button because that's what CSS is for) React checks if it can safely navigate away as a state check.

  • If it can, it lets that happen.
  • If it can't:
    1. it prevents the navigation of occurring via preventDefault(),
    2. does whatever work it needs to do, and then
    3. marks itself as "it is safe to leave now", then retriggers the link.
Burly answered 28/6, 2015 at 3:13 Comment(4)
How is this an answer? The question is "how do I redirect my app on a button click to an external url?". The question generalises to "how to redirect programatically in response to an event?" You can't use a link element for that... – Tedium
A <button> is just another inline HTML element, like span or img or a, and the only thing that makes it a button is CSS. So you take an a, which is designed to link out, and CSS style it to look like your button. Because that's semantically designing your page/app: you have a link, so you make it a link, and then style it to what you need. Instead of doing it backwards, where you start with a button and then try to force it to be a link. Of course all of that is secondary to the still firmly correct advise that you don't need react-router for outbound links, as the error signaled. – Grate
Sorry - I still didn't correctly communicate the challenge. It is not about "making a clickable visible element redirect out". It is about redirecting out after processing an event, in an event handler code. It might be button onClick, or some other event, that activated the code. From within the code, you need to redirect the user to an external url, based on some programmatic logic. The question is asking "is window.location the right way to do this?" – Tedium
And the answer is "no, you use a regular link element". If you have your own question about this, with more details than the original posted asked, feel free to post your own question that refers back to this question. – Grate
S
5

You can try and create a link element and click it from code. This work for me

const navigateUrl = (url) => {

let element = document.createElement('a');

if(url.startsWith('http://') || url.startsWith('https://')){
  element.href =  url;
} else{
  element.href = 'http://' + url;
}

element.click();
}
Shawnshawna answered 10/2, 2021 at 22:46 Comment(0)
B
1

As pointed by @Mike 'Pomax' Kamermans, you can just use to navigate to external link.

I usually do it this way, with is-internal-link

import React from 'react'

import { Link as ReactRouterLink} from 'react-router-dom'
import { isInternalLink } from 'is-internal-link'

const Link = ({ children, to, activeClassName, ...other }) => {
  if (isInternalLink(to)) {
    return (
      <ReactRouterLink to={to} activeClassName={activeClassName} {...other}>
        {children}
      </ReactRouterLink>
    )
  }
  return (
    <a href={to} target="_blank" {...other}>
      {children}
    </a>
  )
}

export default Link

Disclaimer: I am the author of this is-internal-link

Birth answered 13/11, 2018 at 13:43 Comment(0)
S
1

I had the same issue and my research into the issue uncovered that I could simply use an "a href" tag. If using target="_blank" you should write your link this...

<a href="https://yourLink.com" target="_blank" rel="noopener noreferrer">Your Link</a>
Sabbat answered 16/1, 2019 at 21:42 Comment(0)
G
0

I couldn't find a simple way to do that with React Router. As @Mike wrote you should use anchor (<a> tags) when sending the user to external site.

I created a custom <Link> component to dynamically decide whether to render a React-Router <Link> or regular <a> tag.

import * as React from "react";
import {Link, LinkProps} from "react-router-dom";

const ReloadableLink = (props: LinkProps & { forceReload?: boolean }) => {
    const {forceReload, ...linkProps} = props;
    if (forceReload)
        return <a {...linkProps} href={String(props.to)}/>;
    else
        return <Link {...linkProps}>
            {props.children}
        </Link>
};

export default ReloadableLink;
Genarogendarme answered 24/8, 2017 at 15:37 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.