简体   繁体   中英

React Router browserHistory.push open link in a new tab

It is possible to use <link> as

<Link to="route" target="_blank">

to open links in new tab. But is it possible to use browserHistory.push to open links in a new tab?

React-router is build on the browser History API. browserHistory.push calls pushState() method.

From the first line of the linked document:

pushState( ) takes three parameters: A state object, a title (which is currently ignored), and (optionally) a Uniform Resource Locator (URL).

So, the answer to your question is "No".

I've been grouching about it for an hour until I saw Cooper's comment

browserHistory is per tab.

which is so simple but accurate, and made me come up with this solution:

I put it in my App.js (UPDATE: I've added the window blur event because I've encountered a missing keyUp event when cmd+tab or cmd+` to another window.)

  const [metaKeyPressed, setMetaKeyPressed] = useState(false);
  const handleMetaKeyDown = e => {
    if (e.metaKey) {
      setMetaKeyPressed(true);
    }
  };

  const handleMetaKeyUp = e => {
    if (e.metaKey) {
      setMetaKeyPressed(false);
    }
  };

  useEffect(() => {
    document.addEventListener('keydown', handleMetaKeyDown);
    document.addEventListener('keyup', handleMetaKeyUp);
    window.addEventListener('blur', handleMetaKeyUp);
    return () => {
      document.removeEventListener('keydown', handleMetaKeyDown);
      document.removeEventListener('keyup', handleMetaKeyUp);
      window.removeEventListener('blur', handleMetaKeyUp);
    };
  });

Then I have metaKeyPressed which I use to select whether to history.push or window.open (simplified for readability):

const handleRoute = path => { 
    if (metaKeyPressed) {
      window.open(path);
    } else {
      history.push(path);
    }
  };

Also, Consider adding a fix for this issue https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/ I haven't not add this because the browser wants the user to explicitly allow popups when I do.

Building on David's answer, this can be accomplished by wrapping the useHistory hook in a custom one. I renamed mine 'useCtrlHistory' and have this:

import {useEffect, useState} from 'react';
import {useHistory} from "react-router";

export default () => {

  const [ctrlKeyPressed, setCtrlKeyPressed] = useState(false);
  const handleKeyDown = e => {
    if (e.ctrlKey) {
      setCtrlKeyPressed(true);
    }
  };

  const handleKeyUp = e => {
    if (!e.ctrlKey) {
      setCtrlKeyPressed(false);
    }
  };

  const history = useHistory();

  let ctrlHistory = Object.assign({}, history, {
    push: (value) => {
      if (ctrlKeyPressed) {
        window.open(value);
      } else {
        history.push(value);
      }
    }
  });

  useEffect(() => {
    document.addEventListener('keydown', handleKeyDown);
    document.addEventListener('keyup', handleKeyUp);
    window.addEventListener('blur', handleKeyUp);
    return () => {
      document.removeEventListener('keydown', handleKeyDown);
      document.removeEventListener('keyup', handleKeyUp);
      window.removeEventListener('blur', handleKeyUp);
    };
  }, []);

  return ctrlHistory;
};

This will create a new hook that listens for the ctrl key to be pressed. Replace any instances where useHistory.push is present with useCtrlHistory.push and it will detect the ctrl key down.

Starting with react_router 1.0, the props will be passed onto the anchor tag. You can directly use target="_blank". Discussed here: https://github.com/ReactTraining/react-router/issues/2188

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM