首页 > 解决方案 > 一个 btn 的 onClick 我想用 useRef 和 copyToClipboard 获取它的兄弟(输入框)值,将该 btn 的 setState 设置为已复制

问题描述

当用户传递有效链接时,我正在使用缩短 URL API,我获取 API 并使用“map medthod”呈现缩短的 URL 以将它们放入列表中。它在每个映射的“缩短的 URL”旁边都有一个 btn,onClick 我尝试在其中复制到剪贴板并将 btn 的状态从复制更改为已复制。问题是目前它只有在我有 1 个项目时才能正常工作(点击 btn 与 copyToClipboard 一起工作)但如果我有 2 个按钮并且我单击第一个 btn 到 copyToClipboard,它会聚焦映射列表中的最后一个项目并复制(最后一项)第二个 btn 并将所有 btn 的状态设置为复制。我也不明白为什么当我将钥匙传递给控制台时,我在控制台中看不到带有钥匙的 li 标签。有人可以帮我吗。我只想将我单击的 btn 的输入值复制到剪贴板。这里'第一个 btn 的 onCLick 的图像 第二个 btn 获得焦点控制台中没有键的图像 & 显然它们不在列表中? 这是下面的代码

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;

标签: javascriptreactjsapireact-hooksjsx

解决方案


这是因为您对所有链接使用单个 ref

您正在遍历所有链接并提供它们<input ref={inputRef} />。因此 ref 将始终附加到最后一个链接输入

也许不要使用 refs 并使用copyToClipboard像这样的替代功能

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');
  }

或者

使用类似的库react-copy-to-clipboard

也请通过此链接


推荐阅读