Reputation: 385
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
Reputation: 11001
Run the following snippet(s).
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>
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
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
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