diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md index cc56dae1eb..f94acbc701 100644 --- a/docs/useCopyToClipboard.md +++ b/docs/useCopyToClipboard.md @@ -2,13 +2,23 @@ Copy text to a user's clipboard. - ## Usage -Basic usage - ```jsx const Demo = () => { + const [text, setText] = React.useState(''); + const [state, copyToClipboard] = useCopyToClipboard(); + + return ( +
+ setText(e.target.value)} /> + + {state.error + ?

Unable to copy value: {state.error.message}

+ : state.value &&

Copied {state.value}

} +
+ ) + const [text, setText] = React.useState(''); const [copied, copyToClipboard] = useCopyToClipboard(text); @@ -25,11 +35,5 @@ const Demo = () => { ## Reference ```js -const [copied, copyToClipboard] = useCopyToClipboard(text); -const [copied, copyToClipboard] = useCopyToClipboard(text, writeText); +const [state, copyToClipboard] = useCopyToClipboard(); ``` - -, where - -- `writeText` — function that receives a single string argument, which - it copies to user's clipboard. diff --git a/src/__stories__/useCopyToClipboard.story.tsx b/src/__stories__/useCopyToClipboard.story.tsx index 7fcd5a7f3e..3e13b99764 100644 --- a/src/__stories__/useCopyToClipboard.story.tsx +++ b/src/__stories__/useCopyToClipboard.story.tsx @@ -5,19 +5,21 @@ import {useCopyToClipboard} from '..'; const Demo = () => { const [text, setText] = React.useState(''); - const [copied, copyToClipboard] = useCopyToClipboard(text, { - onCopy: txt => alert('success: ' + txt), - onError: err => alert(err), - }); + const [state, copyToClipboard] = useCopyToClipboard(); return (
setText(e.target.value)} /> - -
Copied: {copied ? 'Yes' : 'No'}
-
- -
+ + {state.error + ?

Unable to copy value: {state.error.message}

+ : state.value && ( + <> +

Copied {state.value} {state.noUserInteraction ? 'without' : 'with'} user interaction

+ + + )} +
) } diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 244bcc1bfb..df00969e2a 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,55 +1,49 @@ -import {useState, useCallback, useRef} from 'react'; -import useUpdateEffect from './useUpdateEffect'; +import {useCallback} from 'react'; +import useSetState from './useSetState' import useRefMounted from './useRefMounted'; -const writeTextDefault = require('copy-to-clipboard'); +import * as writeText from 'copy-to-clipboard'; -export type WriteText = (text: string) => Promise; // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText -export interface UseCopyToClipboardOptions { - writeText?: WriteText; - onCopy?: (text: string) => void; - onError?: (error: any, text: string) => void; +export interface CopyToClipboardState { + value?: string, + noUserInteraction: boolean, + error?: Error, } -export type UseCopyToClipboard = (text?: string, options?: UseCopyToClipboardOptions) => [boolean, () => void]; - -const useCopyToClipboard: UseCopyToClipboard = (text = '', options) => { - const {writeText = writeTextDefault, onCopy, onError} = (options || {}) as UseCopyToClipboardOptions; - - if (process.env.NODE_ENV !== 'production') { - if (typeof text !== 'string') { - console.warn('useCopyToClipboard hook expects first argument to be string.'); - } - } +const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] => { const mounted = useRefMounted(); - const latestText = useRef(text); - const [copied, setCopied] = useState(false); - const copyToClipboard = useCallback(async () => { - if (latestText.current !== text) { - if (process.env.NODE_ENV !== 'production') { - console.warn('Trying to copy stale text.'); - } - return; - } + const [state, setState] = useSetState({ + value: undefined, + error: undefined, + noUserInteraction: true + }); + const copyToClipboard = useCallback((value) => { try { - await writeText(text); + if (process.env.NODE_ENV === 'development') { + if (typeof value !== "string") { + console.error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`); + } + } + + const noUserInteraction = writeText(value); + if (!mounted.current) return; - setCopied(true); - onCopy && onCopy(text); + setState({ + value, + error: undefined, + noUserInteraction + }); } catch (error) { if (!mounted.current) return; - console.error(error); - setCopied(false); - onError && onError(error, text); + setState({ + value: undefined, + error, + noUserInteraction: true + }); } - }, [text]); - - useUpdateEffect(() => { - setCopied(false); - latestText.current = text; - }, [text]); + }, []); - return [copied, copyToClipboard]; + return [state, copyToClipboard]; } export default useCopyToClipboard;