skip to Main Content

I’m creating a verification page with a token parameter using react, express and axios

Here’s the page script:

import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import axios from 'axios';
import { Typography } from '@mui/material';

const VerifyPage = () => {
    const { token } = useParams();
    const [verificationResult, setVerificationResult] = useState<any>(null);

    useEffect(() => {
        const verifyEmail = async () => {
            try {
                if (verificationResult) return;
                const res = await axios.get(`http://localhost:5000/api/auth/verify/${token}`);
                console.log(res.data.message);
                if (res && res.data) {
                    setVerificationResult(res.data.message);
                } else {
                    setVerificationResult('Something went wrong');
                    throw new Error('Something went wrong');
                }
                return;
            } catch (error: any) {
                setVerificationResult(error.message);
                return;
            }
        };

        verifyEmail();
    }, [token]);

    return (
        <div id='VerifyPage'>
            {verificationResult ? (
                <Typography variant='h1' align='center' sx={{ mt: 30, fontFamily: 'Noto Sans', color: 'white' }}>
                    <strong>{verificationResult}</strong>
                </Typography>
            ) : (
                <Typography variant='h1' align='center' sx={{ mt: 30, fontFamily: 'Noto Sans', color: 'white' }}>
                    <strong>Verifying...</strong>
                </Typography>
            )}
        </div>
    );
};

export default VerifyPage;

Here’s the api

const verifyEmail = async (verificationToken: string) => {
    try {
        const user = await User.findOne({ verificationToken });
        if (!user) {
            return 'Invalid token';
        }
        if (user.verified) {
            return 'Email already verified';
        }
        await User.updateOne({ verificationToken }, { verified: true });
        return 'Email verified';
    } catch (err) {
        console.log(err);
        return 'Server error';
    }
};

app.get('/api/auth/verify/:verificationToken', async (req: Request, res: Response) => {
    const { verificationToken } = req.params;
    const message = await verifyEmail(verificationToken);
    res.send({ message: message });
});

The user gets sent an email with the link and the token in it. When I open the verification page, useEffect sends multiple axios requests to the server. This result in always showing "Email already verified" (since it verifies it and then tries to do it again).

I tried using conditions, like

if (verificationResult) return;

or even creating new state variables for this like,

const [verified, setVerified] = useState<boolean>(false);

and

if (verified) return;

but nothing changes

Why does that happen and what am I doing wrong? 🙁

EDIT 1
I tried using

if (verificationResult !== null) return;

but it changed nothing.
I also tried to

console.log('UseEffect ran');
console.log(verificationResult); 

at every run and it seems to remain null. I believe it’s because the verifyEmail function is asynchronous. It seems that while the state is null, the useEffect runs multiple times, until it gets a response (2 at the end) from the server.
How can I fix this uncontrolled calling of useEffect (Maybe make it wait until it gets the response from the server)?
Btw, thank you for your help <3

2

Answers


  1. Chosen as BEST ANSWER

    Found the problem! After some researches, I found that this happens only in dev mode. This is because from React 18, useEffect is called twice, even if it has empty dependency array. I fixed this with removing <React.StrictMode> from index.ts

    const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
    root.render(
       <React.StrictMode>
            <App />
       </React.StrictMode>
    );
    

    More info here: React Hooks: useEffect() is called twice even if an empty array is used as an argument


  2. You are correct Alex, that happened to me in my previous project.
    If you check the following link: React docs – Strict Mode, you will see that it specifically mentions:

    • Your components will re-render an extra time to find bugs caused by impure rendering.
    • Your components will re-run Effects an extra time to find bugs caused by missing Effect cleanup.
    • Your components will be checked for usage of deprecated APIs.

    Best practice is to add a flag to only run the API call only when the component is fully rendered:

    1. Additional useEffect to update a boolean value
    2. Check the boolean value to trigger API call
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search