0

I'm using a shorten URL API when the user passes a valid link, i fetch API and render the shortened URL with "map medthod" to make them into a list. It has a btn next to each mapped "shortened URL" where onClick i try to copyToClipboard and change state of btn from Copy to Copied. The problem is currently it only works fine if i have 1 item(on click btn works fine with copyToClipboard) but if i have 2 buttons and i click the very 1st btn to copyToClipboard it's focusing the last item in mapped list and copying the value of (last item) 2nd btn and also setting state for all btns to copied. I also don't understand why i can't see li tags with keys in console when i pass them the keys. can someone help me out. I just want to copyToClipboard that input value of the btn i have clicked. here's what it looks like - image of onCLick of 1st btn 2nd btn gets focus & image of no keys in console & apparently they aren't in a list? Here is the code below

import { useForm } from "react-hook-form";
import axios from 'axios';
import Loading from '../../images/Ripple-1s-200px.svg';


const Shorten = () => {
    // get built in props of react hook form i.e. register,handleSubmit & errors / watch is for devs
    const { register, handleSubmit, formState: {errors} } = useForm();

    //1. set user original values to pass as params to url
    const [link, setLink] = useState('');

    //2. set loader initial values to false 
    const [loading, setLoading] = useState(false);

    //3. pass the fetched short link object into an array so we can map
    const [displayLinks, setDisplayLinks] = useState([]);

    //4. onSubmit form log data into link & showLoader for a breif moment
    const onSubmit = (data, event) => {
                event.preventDefault();
                //puttin data in a variable to pass as url parameter if valid
                setLink(data.userLink);
                //add loading here after data is set to state
                setLoading(!false);
            }

    //5. fetch the shortened url link using async method to show loading
    useEffect(() => {
        let unmounted = false;
        async function makeGetRequest() {
            try {
                let res = await axios.get('https://api.shrtco.de/v2/shorten', { params: { url: link } });
                //hid loader if u get response from api call
                    if (!unmounted && res.data.result.original_link) {
                        setLoading(false);

                        //add the data to displayLinks array to map
                        return setDisplayLinks(displayLinks => [...displayLinks, res.data.result]);
                    }

                } 
            catch (error) {
                console.log(error, "inital mount request with no data");
            }

        }
        //invoke the makeGetRequest here 
        makeGetRequest();
       
        return () => {
            unmounted = true;
        }

        //passing dependency to re-render on change of state value
    }, [link]);



    //6. intial State of copied or not button
    const [copySuccess, setCopySuccess] = useState('Copy');

    const inputRef = useRef(null);

    //7. onCick of button target it's short url right now it's selecting the last element 
    const copyToClipboard = (e) => {
        e.preventDefault();
        inputRef.current.select();
        document.execCommand('copy');
        // This is just personal preference.
        setCopySuccess('Copied');
      };

      console.log(displayLinks);
    return (
        <div>
            <form onSubmit={handleSubmit(onSubmit)}>
                <label></label>
                <input
                    {...register("userLink", {required: "Please add a link"})}
                    type="url"
                    id="userLink"
                />
                {errors.userLink && <span>{errors.userLink.message}</span>}
                <input type="submit" />
            </form>
            {
                loading ?
                    <div className="loader" id="loader">
                        <img src={Loading} alt="Loading" />
                    </div>
                        :   <ul>
                                {
                                    displayLinks.map((el) => {
                                        return (
                                            <li key={el.code}>
                                                <div>
                                                    <h5>{el.original_link}</h5>
                                                </div>
                                                {
                                                    /* Logical shortcut for only displaying the 
                                                    button if the copy command exists */
                                                    document.queryCommandSupported('copy') &&
                                                <form>
                                                    <input
                                                        ref={inputRef}
                                                        defaultValue={el.full_short_link}>
                                                    </input>

                                                    <button onClick={copyToClipboard}>{copySuccess}</button> 
                                                         
                                                </form>
                                                }
                                            </li>
                                        )
                                    })
                                }
                            </ul>
            }
        </div>
    )
}

export default Shorten;
Akii J
  • 57
  • 1
  • 10

1 Answers1

0

Its because you are using a single ref for all the links

You are looping over all the links and giving their <input ref={inputRef} />.So the ref will always be attached to the last link input

Maybe don't use refs and use an alternative copyToClipboard function like this one

const copyToClipboard = (url) => {
    const textField = document.createElement('textarea')
    textField.innerText = url
    document.body.appendChild(textField)
    if (window.navigator.platform === 'iPhone') {
      textField.setSelectionRange(0, 99999)
    } else {
      textField.select()
    }
    document.execCommand('copy')
    textField.remove()
    
    setCopySuccess('Copied');
  }

OR

Use a library like react-copy-to-clipboard

Also please go through this link

Akhil
  • 879
  • 5
  • 11
  • Yes, i used that library and fixed it in the morning, I removed the ref and split the component into linksList & links for better readibility. Thank you. – Akii J May 29 '21 at 08:14