Search code examples
reactjsreact-hooksmaterial-uialertuse-effect

Trying to write a react hook that will copy a string, then show an alert. It works but only runs once


As a part of my website i'm trying to make it to where I have buttons that the user can click and it will copy my contact information to the clipboard- then show an alert that verifies that the item has been copied.

I'm utilizing material-ui for the snackbar and react-use-clipboard to copy the address. What I have below sort of works. The buttons work great as far as copying the addresses. That works flawlessly and endlessly.

However, the alerts are the main problem.

The first problem is that both alerts appear when the app loads the first time and I only want them to show when the button is pushed.

The second problem is that they do work when a button is pushed- but only the first time. If I click 'copy e-mail' it copies the e-mail and gives an alert. If I then click 'copy linkedin' it copies the linkedin address and gives an alert. If I then go back and hit copy e-mail again, it will copy the e-mail but will not give an alert.

I can't figure out why the hook wouldn't run the alert more then once. Any suggestions would be much appreciated.

function Contact() {

const [isCopiedEmail, setCopiedEmail] = useClipboard("[email protected]");

useEffect(() => {
    setOpenEmail(true);
    console.log("Email")}, 
    [isCopiedEmail]
    )

const [isCopiedLinkedIn, setCopiedLinkedIn] = useClipboard("https://www.linkedin.com/in/mylinkedinaddress");

useEffect(() => {
    setopenLinkedIn(true);
    console.log("LinkedIn")}, 
    [isCopiedLinkedIn]
    )

function Alert(props) {
    return <MuiAlert elevation={6} variant="filled" {...props} />;
  }
  
  const useStyles = makeStyles((theme) => ({
    root: {
      width: '100%',
      '& > * + *': {
        marginTop: theme.spacing(2),
      },
    },
  }));

  const [openLinkedIn, setopenLinkedIn] = useState(false);

  const handleCloseLinkedIn = (event, reason) => {
      if (reason === 'clickaway') {
      return;
  }
  setopenLinkedIn(false);
};

  const [openEmail, setOpenEmail] = useState(false);

  const handleCloseEmail = (event, reason) => {
    if (reason === 'clickaway') {
      return;
    }
    setOpenEmail(false);
  };

return (
    <Container className="background3">
    <Row>
        </Col>
        <Col xs={12} md={4}>
                <Button variant="contained" size="large" color="secondary" onClick={setCopiedEmail}>Copy to Clipboard</Button>
        </Col>
        <Col xs={12} md={4}>
         <Button variant="contained" size="large" color="secondary" onClick={setCopiedLinkedIn}>Copy to Clipboard</Button>

        </Col>

        <Snackbar open={openLinkedIn} autoHideDuration={2000} onClose={handleCloseLinkedIn}>
        <Alert onClose={handleCloseLinkedIn} severity="success">
        My LinkedIn URL successfully copied!
        </Alert>
        </Snackbar>

        <Snackbar open={openEmail} autoHideDuration={2000} onClose={handleCloseEmail}>
        <Alert onClose={handleCloseEmail} severity="success">
        My e-mail address successfully copied!
        </Alert>
        </Snackbar>
        
    </Row>
    </Container>
)
}

export default Contact

Solution

  • The alerts are appearing when your app loads because of these effects:

      useEffect(() => {
        setOpenEmail(true);
        console.log("Email");
      }, [isCopiedEmail]);
    

    I'm focusing on isCopiedEmail, but the same applies to isCopiedLinkedIn. This runs when the component mounts, no matter what the value of isCopiedEmail. And, you never reset the value of isCopiedEmail, so the effect will never run again after it becomes true.

    Since you're already calling a function when you're calling setCopiedEmail, I would call setOpenEmail(true) at the same time, and remove these effects altogther:

    function Contact() {
      const [isCopiedEmail, setCopiedEmail] = useClipboard(
        "[email protected]"
      );
    
      const [openEmail, setOpenEmail] = useState(false);
      const handleCloseEmail = (event, reason) => {
        if (reason === "clickaway") {
          return;
        }
        setOpenEmail(false);
      };
    
      const handleCopiedEmail = () => {
        setCopiedEmail();
        setOpenEmail(true);
      };
    
      // ...
    
      return (
        <Container className="background3">
          <Row>
            <Col xs={12} md={4}>
              <Button
                variant="contained"
                size="large"
                color="secondary"
                onClick={handleCopiedEmail}
              >
                Copy to Clipboard
              </Button>
            </Col>
            {/* ... */}
          </Row>
        </Container>
      );
    }