Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React copy to clipboard using useRef hook

I'm trying to handle copy to clipboard in React with useRef hook. How can I achieve it without any additional libraries? Here is my code but it throws an error myRef.current.select is not a function.

import React, { useRef } from "react";

const Element = () => {
  const myRef = useRef();

  const copyToClipboard = () => {
    myRef.current.select();
    document.execCommand("copy");
  };

  return (
    <div>
      <span onClick={copyToClipboard} ref={myRef}>
        Text to be copied
      </span>
    </div>
  );
};

export default Element;

like image 662
Dev Avatar asked Aug 23 '20 12:08

Dev


People also ask

How do I copy text to clipboard?

Select the text or graphics you want to copy, and press Ctrl+C. Each selection appears in the Clipboard, with the latest at the top. Optionally, repeat step 2 until you've copied all the items you want to use. Tip: After you open the Clipboard, it stores content that you copy or cut from anywhere.

How use useRef hook in React JS?

The useRef Hook allows you to persist values between renders. It can be used to store a mutable value that does not cause a re-render when updated. It can be used to access a DOM element directly.


3 Answers

Run the following snippet(s).

  1. Have a separate CopyToClipElement component (with React Hooks) which handles the copy for given text prop. Use this component in your render.

const CopyToClipElement = ({ text }) => {
  const myRef = React.useRef(null);
  const [data, setData] = React.useState(text);
  React.useEffect(() => setData(text), [text]);

  React.useEffect(() => {
    if (myRef.current && data) {
      myRef.current.select();
      document.execCommand("copy");
      setData(null);
    }
  }, [data, myRef.current]);

  return <div>{data && <textarea ref={myRef}>{data}</textarea>}</div>;
};

const Element = () => {
  const [copyText, setCopyText] = React.useState("");
  const data = ["Text to be copied", "Copy foo"];

  return (
    <div>
      {data.map((text) => (
        <span
          style={{ margin: "10px", cursor: "pointer", color: 'blue' }}
          onClick={() => setCopyText(text)}
        >
          {text}
        </span>
      ))}
      <CopyToClipElement text={copyText} />
    </div>
  );
};

const domContainer = document.querySelector('#app');
ReactDOM.render(<Element/>, domContainer);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="app"> </div>
  1. Alternatively, Just add the utility method copyToClipboard in plain JS.

const copyToClipboard = (text: string) => {
  const ta = document.createElement("textarea");
  ta.innerText = text;
  document.body.appendChild(ta);
  ta.select();
  document.execCommand("copy");
  ta.remove();
};

const Element = () => {
  const data = ["Text to be copied", "Copy Bar"];

  return (
    <div>
      {data.map((text) => (
        <span
          style={{ margin: "10px", cursor: "pointer", color: 'red' }}
          onClick={() => copyToClipboard(text)}
        >
          {text}
        </span>
      ))}
    </div>
  );
};

const domContainer = document.querySelector('#app');
ReactDOM.render(<Element />, domContainer);
    <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

    <div id="app"> </div>
like image 121
Siva K V Avatar answered Oct 01 '22 23:10

Siva K V


Take a look at this solution, I just adapted it to your use case.

  const copyToClipboard = () => {
    const range = document.createRange();
    range.selectNode(myRef.current);
    window.getSelection().addRange(range);

    try {
      // Now that we've selected the anchor text, execute the copy command
      const successful = document.execCommand('copy');
      const msg = successful ? 'successful' : 'unsuccessful';
      console.log('Copy email command was ' + msg);
    } catch(err) {
      console.log('Oops, unable to copy');
    }

    // Remove the selections - NOTE: Should use
    // removeRange(range) when it is supported
    window.getSelection().removeAllRanges();
  };
like image 44
Nazar Litvin Avatar answered Oct 02 '22 00:10

Nazar Litvin


Ciao, I found a solution but I had to use input instead of span. And I haven't used useRef hook but just a variable (let myInput = null;) and then on input ref I wrote ref={(ref) => myInput = ref}. In this way, your copyToClipboard works.

The code:

function App() {
  let myInput = null;
  const copyToClipboard = () => {
    myInput.select();
    document.execCommand("copy");
    alert("Copied the text: " + myInput.value);
  };

  return (
    <div>
      <input readOnly value="Text to copy" onClick={copyToClipboard} ref={(ref) => myInput = ref} />
    </div>
  );
}

Here the codesandbox example.

like image 45
Giovanni Esposito Avatar answered Oct 01 '22 22:10

Giovanni Esposito