I have this React template page:
import Button from '@mui/material/Button';
import { useNavigate, useParams } from 'react-router-dom';
import Box from '@mui/material/Box';
import Divider from '@mui/material/Divider';
import Typography from '@mui/material/Typography';
import FuseSvgIcon from '@fuse/core/FuseSvgIcon';
import format from 'date-fns/format';
// eslint-disable-next-line import/order
import { getRandomBackgroundImage } from '../utils';
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';
import { queryAPIKeyByID } from 'app/api/queries/api-keys';
import FuseLoading from '@fuse/core/FuseLoading';
function APIKeyView() {
const { id: APIKeyID } = useParams();
const [copied, setCopied] = useState(false);
const { data: apiKeyData, isLoading } = useQuery({
queryKey: ['APIKeyByID', APIKeyID],
queryFn: () => queryAPIKeyByID(APIKeyID),
});
const navigate = useNavigate();
if (isLoading) {
return (
<div className="flex items-center justify-center w-full h-screen">
<FuseLoading />
</div>
);
}
const handleCopy = () => {
navigator.clipboard.writeText(apiKeyData.api_token);
setCopied(true);
// Reset label back to "Copy" after 2 seconds
setTimeout(() => setCopied(false), 2000);
};
return (
<>
<Box
className="relative w-full h-160 sm:h-192 px-32 sm:px-48"
sx={{
backgroundColor: 'background.default',
}}
>
<img
className="absolute inset-0 object-cover w-full h-full"
src={getRandomBackgroundImage()}
alt="user background"
/>
</Box>
<div className="relative flex flex-col flex-auto items-center p-24 pt-0 sm:p-48 sm:pt-0">
<div className="w-full max-w-3xl">
<div className="flex flex-auto items-end">
<div className="flex items-center ml-auto relative bottom-20">
<Button
variant="contained"
color="secondary"
onClick={() => navigate('edit', { state: apiKeyData })}
>
<FuseSvgIcon size={20}>heroicons-outline:pencil-alt</FuseSvgIcon>
<span className="mx-8">Edit</span>
</Button>
</div>
</div>
<Typography className="mt-12 text-4xl font-bold truncate">
{apiKeyData.username}
</Typography>
<Divider className="my-32" />
<div className="flex flex-col space-y-32">
{apiKeyData.email && (
<div className="flex items-center gap-24 leading-6">
<FuseSvgIcon>heroicons-outline:mail</FuseSvgIcon>
<a
className="hover:underline text-primary-500"
href={`mailto: ${apiKeyData.email}`}
target="_blank"
rel="noreferrer"
>
{apiKeyData.email}
</a>
</div>
)}
{apiKeyData.api_token && (
<div className="flex items-center gap-24 leading-6">
<FuseSvgIcon>heroicons-outline:key</FuseSvgIcon>
{`${apiKeyData.api_token.substring(0, 9) } .... ${ apiKeyData.api_token.substring(apiKeyData.api_token.length - 4)}`}
<Button
onClick={handleCopy}
className="ml"
>
{copied ? "Copied!" : "Copy"}
</Button>
</div>
)}
</div>
</div>
</div>
</>
);
}
export default APIKeyView;
I added a button to copy the api_key value into clipboard. But when I press the copy button API request is made to get the data again. This action is incorrect. I just need to copy the text. Do you know how I can prevent this?
2
Answers
It seems the unnecessary re-render may happen when the user click the copy button.
Try this code to prevent the unnecessary re-render.
The problem is probably caused by a rerender updating the
useQuery
parameters, storing the parameter as a memo should fix the problemThis way the query will run once (twice on debug mode) and whenever the value of
APIKeyID
changes.