skip to Main Content

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


  1. It seems the unnecessary re-render may happen when the user click the copy button.
    Try this code to prevent the unnecessary re-render.

    const handleCopy = React.useCallback(() => {
      if (apiKeyData?.api_token) {
        navigator.clipboard.writeText(apiKeyData.api_token);
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
      }
    }, [apiKeyData]);
    
    Login or Signup to reply.
  2. The problem is probably caused by a rerender updating the useQuery parameters, storing the parameter as a memo should fix the problem

      const queryParameter = useMemo(() => {
        queryKey: ['APIKeyByID', APIKeyID],
        queryFn: () => queryAPIKeyByID(APIKeyID),
      }, [APIKeyID]);
      const { data: apiKeyData, isLoading } = useQuery(queryParameter);
    

    This way the query will run once (twice on debug mode) and whenever the value of APIKeyID changes.

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