skip to Main Content

I’m building a MERN stack app in which a user has to verify their account using a link sent to their email address.
After the user clicks on the link, they are sent to this ActivationPage component. Here, I extract the token from the URL and, in a POST request in useEffect, send it to the server where the user is created in the database (MongoDB).

Now the issue is that when the component first runs, it returns "Your account has been activated successfully!" as it should, however, after a few seconds the message changes to "Your token has expired!" because useEffect ran again and tried to created the user again which resulted in an error and hence the catch block ran. So, I basically need a way to make useEffect run only once.

import React, { useState, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import axios from 'axios';

import { serverUrl } from '../serverUrl';

const ActivationPage = () => {
  const [error, setError] = useState(false);

  const [searchParams] = useSearchParams();
  const activationToken = searchParams.get('token');

  useEffect(() => {
    const activationEmail = async () => {
      if (activationToken) {
        try {
          const response = await axios.post(`${serverUrl}/user/activation`, { activationToken });
          console.log(response);
        } catch (error) {
          setError(true);
          console.log(`ERROR: ${error.message}`);
        }
      }
    };

    activationEmail();
  }, []);

  return (
    <div className='w-full h-screen flex justify-center items-center'>
      {error ? <p>Your token has expired!</p> : <p>Your account has been activated successfully!</p>}
    </div>
  );
};

export default ActivationPage;

2

Answers


  1. I would use a ref to store a flag if it was already run.
    The reason for using useRef instead of useState is that we just need a persistent variable and don’t want or need to trigger a rerender when changing its value.

    import React, { useState, useEffect, useRef } from 'react';
    import { useSearchParams } from 'react-router-dom';
    import axios from 'axios';
    
    import { serverUrl } from '../serverUrl';
    
    const ActivationPage = () => {
      const [error, setError] = useState(false);
    
      const [searchParams] = useSearchParams();
      const activationToken = searchParams.get('token');
      const wasAlreadyRequested = useRef(false);
    
      useEffect(() => {
        const activationEmail = async () => {
          if (activationToken) {
            try {
              wasAlreadyRequested.current = true
              const response = await axios.post(`${serverUrl}/user/activation`, { activationToken });
              console.log(response);
            } catch (error) {
              setError(true);
              console.log(`ERROR: ${error.message}`);
            }
          }
        };
        if (!wasAlreadyRequested.current){
         activationEmail();
        }
      }, [wasAlreadyRequested]);
    
      return (
        <div className='w-full h-screen flex justify-center items-center'>
          {error ? <p>Your token has expired!</p> : <p>Your account has been activated successfully!</p>}
        </div>
      );
    };
    
    export default ActivationPage;
    
    Login or Signup to reply.
  2. this is a the useeffect code
    use this code

      useEffect(() => {
        first
        return () => {
          second
        }
      }, [third])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search