skip to Main Content

I am trying to build a component with some validations depending on user’s clipboard.

import { useState } from 'react'

export default function useCopiedFromClipboard() {
  const [copiedData, setCopiedData] = useState<string>('')

    navigator.clipboard
      .readText()
      .then((text) => {
        setCopiedData(text)
      })
      .catch((err) => {
        console.error('Failed to read clipboard contents: ', err)
      })

  return { copiedData }
}

So what I really want to do is use this hook inside a component.

import React from 'react'

export default function MyComponent() {
  const {copiedData} = useCopiedFromClipboard()
  
  useEffect(()=>{
    console.log('Run')
  },[copiedData])
  
  return (
    <></>
  )
}

I am expecting this useEffect to run everytime the user copies something to their clipboard. But this is not working.

Does anyone have any idea of how to implement this one ?

3

Answers


  1. The reason is that your useCopiedFromClipboard hook only reads the clipboard content once, when the component mounts, and does not update it when the clipboard changes. Therefore, your useEffect hook does not run again when the copiedData state changes.

    To fix this, you need to use an event listener to monitor the clipboard changes, and update the copiedData state accordingly. You can use the Clipboard API to do this, which provides a copy event that fires when the user copies something to their clipboard.

    For example:

    function useCopiedFromClipboard() {
      const [copiedData, setCopiedData] = useState("");
    
      const handleCopy = async (e) => {
        const text = await navigator.clipboard.readText();
        setCopiedData(text);
      };
    
      useEffect(() => {
        document.addEventListener("copy", handleCopy);
        return () => {
          document.removeEventListener("copy", handleCopy);
        };
      }, []);
    
      return { copiedData };
    }
    

    You can see the whole example here.

    Login or Signup to reply.
  2. import { useState, useEffect } from 'react'
    
    export default function useCopiedFromClipboard() {
      const [copiedData, setCopiedData] = useState<string>('')
    
      useEffect(() => {
        const handleClipboardChange = () => {
          navigator.clipboard
            .readText()
            .then((text) => {
              setCopiedData(text)
            })
            .catch((err) => {
              console.error('Failed to read clipboard contents: ', err)
            })
        }
    
        document.addEventListener('paste', handleClipboardChange)
    
        return () => {
          document.removeEventListener('paste', handleClipboardChange)
        }
      }, [])
    
      return { copiedData }
    }
    

    Hope this helps you

    Login or Signup to reply.
  3. The problem with your code is that you’re only getting the value of the clipboard once, when you initialize the useCopiedFromClipboard hook for the first time. It does not ‘listen’ for more clipboard events.

    We can solve this by using a useEffect where we setup a listener on the copy event. When the event first we manually get the selected text in the document and set that value to the users clipboard. Then we can update the state with the same value.

    import { useState } from "react";
    
    export default function useCopiedFromClipboard() {
      const [copiedData, setCopiedData] = useState<string>("");
    
      useEffect(() => {
        const handleCopy = (e: ClipboardEvent) => {
          const selection = document.getSelection() ?? "";
          const data = selection.toString();
          e.clipboardData?.setData("text/plain", data);
          setCopiedData(data);
        };
    
        document.addEventListener("copy", handleCopy);
    
        return () => {
          document.removeEventListener("copy", handleCopy);
        };
      }, []);
    
      return { copiedData };
    }
    

    You can add some try catch as you wish, this should cover the bare logic.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search