Dev
Dev

Reputation: 385

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;

Upvotes: 7

Views: 11450

Answers (3)

Siva Kondapi Venkata
Siva Kondapi Venkata

Reputation: 11001

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>

Upvotes: 5

Giovanni Esposito
Giovanni Esposito

Reputation: 11156

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.

Upvotes: 1

Nazar Litvin
Nazar Litvin

Reputation: 778

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();
  };

Upvotes: 2

Related Questions