英文:
Force useEffect to run only once
问题
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 create 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;
英文:
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;
答案1
得分: 3
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;
英文:
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;
答案2
得分: 0
以下是翻译好的部分:
这是一个 useEffect 代码
使用这段代码
useEffect(() => {
first
return () => {
second
}
}, [third])
英文:
this is a the useeffect code
use this code
useEffect(() => {
first
return () => {
second
}
}, [third])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论