From 90d0a5ddb6bfc9e35ec9671227a290636279398c Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Mon, 8 Apr 2019 21:17:40 +1000 Subject: [PATCH 1/2] Improved useCopyToClipboard --- docs/useCopyToClipboard.md | 24 ++++--- src/__stories__/useCopyToClipboard.story.tsx | 20 +++--- src/useCopyToClipboard.ts | 75 ++++++++++---------- 3 files changed, 61 insertions(+), 58 deletions(-) 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..c66c339b8b 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,55 +1,52 @@ -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; +const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] => { + const mounted = useRefMounted(); + const [state, setState] = useSetState({ + value: undefined, + error: undefined, + noUserInteraction: true + }); - if (process.env.NODE_ENV !== 'production') { - if (typeof text !== 'string') { - console.warn('useCopyToClipboard hook expects first argument to be string.'); - } - } + const copyToClipboard = useCallback((value) => { + try { + if (!value) { + throw new Error('No value to copy to clipboard') + } - 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.'); + if (typeof value !== "string") { + throw new Error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`); } - return; - } - try { - await writeText(text); + 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); - } - }, [text]); - useUpdateEffect(() => { - setCopied(false); - latestText.current = text; - }, [text]); + setState({ + value: undefined, + error, + noUserInteraction: true + }); + } + }, []); - return [copied, copyToClipboard]; + return [state, copyToClipboard]; } export default useCopyToClipboard; From 8b04ef0d6d8a36517cc08985fec4e1659ea46517 Mon Sep 17 00:00:00 2001 From: Ward Oosterlijnck Date: Mon, 8 Apr 2019 23:15:15 +1000 Subject: [PATCH 2/2] Type errors only in development --- src/useCopyToClipboard.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index c66c339b8b..df00969e2a 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -19,17 +19,15 @@ const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] = const copyToClipboard = useCallback((value) => { try { - if (!value) { - throw new Error('No value to copy to clipboard') - } - - if (typeof value !== "string") { - throw new Error(`Cannot copy typeof ${typeof value} to clipboard, must be a string`); + 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; + if (!mounted.current) return; setState({ value, error: undefined, @@ -37,7 +35,6 @@ const useCopyToClipboard = (): [CopyToClipboardState, (value: string) => void] = }); } catch (error) { if (!mounted.current) return; - setState({ value: undefined, error,