在ReactJS中,如何将文本复制到剪贴板?

466

我正在使用ReactJS,当用户点击链接时,我想要将一些文本复制到剪贴板。

我正在使用Chrome 52,不需要支持其他任何浏览器。

我看不出来为什么这段代码没有将数据复制到剪贴板。(代码片段来源于Reddit帖子。)

我做错了吗?有人能建议实现使用ReactJS复制到剪贴板的“正确”方法吗?

copyToClipboard = (text) => {
  console.log('text', text)
  var textField = document.createElement('textarea')
  textField.innerText = text
  document.body.appendChild(textField)
  textField.select()
  document.execCommand('copy')
  textField.remove()
}

1
你是否尝试过使用第三方解决方案,比如 https://clipboardjs.com/ 或 https://github.com/zeroclipboard/zeroclipboard? - EugZol
54
我更喜欢编写代码而不是添加另一个依赖项,前提是代码相当简短。 - Duke Dougal
请查看这些答案:https://dev59.com/fHRC5IYBdhLWcg3wJNcN - elmeister
@elmeister 这个问题是关于ReactJS的。 - Duke Dougal
我们难道只是为了复制而使用库吗?真的吗? - codemon
31个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
8

您的代码应该能够完美工作,我也使用相同的方式。但请确保如果点击事件是从弹出屏幕(如Bootstrap模态框)中触发的,那么所创建的元素必须在该模态框内,否则将无法复制。您可以始终给该模态框内的某个元素指定一个ID(作为第二个参数),并使用getElementById检索它,然后将新创建的元素附加到该元素而不是文档中。代码示例:

copyToClipboard = (text, elementId) => {
  const textField = document.createElement('textarea');
  textField.innerText = text;
  const parentElement = document.getElementById(elementId);
  parentElement.appendChild(textField);
  textField.select();
  document.execCommand('copy');
  parentElement.removeChild(textField);
}

8

使用Material UI的完全工作的React组件

为了更好地理解,我也准备了一个CodeSandbox。希望能帮到你。

import { useState } from "react";
import { IconButton, Snackbar } from "@mui/material";
import ShareIcon from "@mui/icons-material/Share";

const CopyToClipboardButton = () => {
  const [open, setOpen] = useState(false);

  const handleClick = () => {
    setOpen(true);
    navigator.clipboard.writeText(window.location.toString());
  };

  return (
    <>
      <IconButton onClick={handleClick} color="primary">
        <ShareIcon />
      </IconButton>
      <Snackbar
        message="Copied to clibboard"
        anchorOrigin={{ vertical: "top", horizontal: "center" }}
        autoHideDuration={20000}
        onClose={() => setOpen(false)}
        open={open}
      />
    </>
  );
};

export default CopyToClipboardButton;

这是按钮的外观:

enter image description here

当您单击它时:

enter image description here

来源:https://fwuensche.medium.com/react-button-to-copy-to-clipboard-75ef5ecdc708


8

无需安装第三方包,我尽可能地保持简单。这对我很有效。

import React, { useState } from "react"    
function MyApp() {
    const [copySuccess, setCopySuccess] = useState(null);
    const copyToClipBoard = async copyMe => {
       try {
           await navigator.clipboard.writeText(copyMe);
           setCopySuccess('Copied!');
       } 
       catch (err) {
           setCopySuccess('Failed to copy!');
       }
    };
     
    return (
        <button onClick={(e) => copyToClipBoard(what you want to copy goes here)} >
           My button
        </button>    
       )
    }

7

首先创建BTN,然后添加此onClick:

onClick={() =>  navigator.clipboard.writeText(textState)}
或者
onClick={() =>  navigator.clipboard.writeText('Your text for copy')}

7
这对我有效:
const handleCopyLink = useCallback(() => {
    const textField = document.createElement('textarea')
    textField.innerText = url
    document.body.appendChild(textField)
    if (window.navigator.platform === 'iPhone') {
      textField.setSelectionRange(0, 99999)
    } else {
      textField.select()
    }
    document.execCommand('copy')
    textField.remove()
  }, [url])

1
结尾处的提醒是个不错的点子。不过最好加个toastId,这样如果用户点击超过一次就不会在UI上收到太多通知了。虽然这对于本问题来说并不是特别相关。 - Daniel
这里完全不涉及Toast,而且在回调函数中也超出了上下文范围,因为原始发布者无法知道它在哪里以及如何定义。 - trainoasis
修复:Toast已移除。 - Antonio Carlos Araújo

6

简明扼要的答案是:

navigator.clipboard.writeText("value")

该代码用于将指定的文本值复制到用户剪贴板中。

4
使用此命令将您的值传递给函数。
var promise = navigator.clipboard.writeText(newClipText)

在jsx组件中未定义navigator.clipboard - e-info128

3

对于那些想从 DIV 中选择而不是文本字段中选择的人,这里是代码。代码很容易理解,如果您需要更多信息,请在此处发表评论:

     import React from 'react';
     ....

    //set ref to your div
          setRef = (ref) => {
            // debugger; //eslint-disable-line
            this.dialogRef = ref;
          };

          createMarkeup = content => ({
            __html: content,
          });

    //following function select and copy data to the clipboard from the selected Div. 
   //Please note that it is only tested in chrome but compatibility for other browsers can be easily done

          copyDataToClipboard = () => {
            try {
              const range = document.createRange();
              const selection = window.getSelection();
              range.selectNodeContents(this.dialogRef);
              selection.removeAllRanges();
              selection.addRange(range);
              document.execCommand('copy');
              this.showNotification('Macro copied successfully.', 'info');
              this.props.closeMacroWindow();
            } catch (err) {
              // console.log(err); //eslint-disable-line
              //alert('Macro copy failed.');
            }
          };

              render() {
                    return (
                        <div
                          id="macroDiv"
                          ref={(el) => {
                            this.dialogRef = el;
                          }}
                          // className={classes.paper}
                          dangerouslySetInnerHTML={this.createMarkeup(this.props.content)}
                        />
                    );
            }

3

navigator.clipboard在http连接中无法使用,根据他们的文档。因此,您可以检查它是否未定义并使用document.execCommand('copy')替代,这个解决方案应该覆盖几乎所有浏览器。

const defaultCopySuccessMessage = 'ID copied!'

const CopyItem = (props) => {
  const { copySuccessMessage = defaultCopySuccessMessage, value } = props

  const [showCopySuccess, setCopySuccess] = useState(false)


  function fallbackToCopy(text) {
    if (window.clipboardData && window.clipboardData.setData) {
      // IE specific code path to prevent textarea being shown while dialog is visible.
      return window.clipboardData.setData('Text', text)
    } else if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
      const textarea = document.createElement('textarea')
      textarea.innerText = text
      // const parentElement=document.querySelector(".up-CopyItem-copy-button")
      const parentElement = document.getElementById('copy')
      if (!parentElement) {
        return
      }
      parentElement.appendChild(textarea)
      textarea.style.position = 'fixed' // Prevent scrolling to bottom of page in MS Edge.
      textarea.select()
      try {
        setCopySuccess(true)
        document.execCommand('copy') // Security exception may be thrown by some browsers.
      } catch (ex) {
        console.log('Copy to clipboard failed.', ex)
        return false
      } finally {
        parentElement.removeChild(textarea)
      }
    }
  }

  const copyID = () => {
    if (!navigator.clipboard) {
      fallbackToCopy(value)
      return
    }
    navigator.clipboard.writeText(value)
    setCopySuccess(true)
  }

  return showCopySuccess ? (
    <p>{copySuccessMessage}</p>
  ) : (
    <span id="copy">
      <button onClick={copyID}>Copy Item </button>
    </span>
  )
}

你可以在任何地方调用和重用这个组件

const Sample=()=>(
   <CopyItem value="item-to-copy"/>
)

2
import React, { Component } from 'react';

export default class CopyTextOnClick extends Component {
    copyText = () => {
        this.refs.input.select();

        document.execCommand('copy');

        return false;
    }

    render () {
        const { text } = this.state;

        return (
            <button onClick={ this.copyText }>
                { text }

                <input
                    ref="input"
                    type="text"
                    defaultValue={ text }
                    style={{ position: 'fixed', top: '-1000px' }} />
            </button>
        )
    }
}

execCommand已被弃用。 - anshul
感谢@anshul的关注,您能否编辑一下备选方案。也许可以参考https://developer.mozilla.org/en-US/docs/Web/API/Selection。 - Yash Pokar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,