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;