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
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
More info here: React Hooks: useEffect() is called twice even if an empty array is used as an argument
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:
Best practice is to add a flag to only run the API call only when the component is fully rendered: