Force useEffect to run only once.

huangapple go评论68阅读模式
英文:

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 &#39;react&#39;;
import { useSearchParams } from &#39;react-router-dom&#39;;
import axios from &#39;axios&#39;;

import { serverUrl } from &#39;../serverUrl&#39;;

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

  const [searchParams] = useSearchParams();
  const activationToken = searchParams.get(&#39;token&#39;);

  useEffect(() =&gt; {
    const activationEmail = async () =&gt; {
      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 (
    &lt;div className=&#39;w-full h-screen flex justify-center items-center&#39;&gt;
      {error ? &lt;p&gt;Your token has expired!&lt;/p&gt; : &lt;p&gt;Your account has been activated successfully!&lt;/p&gt;}
    &lt;/div&gt;
  );
};

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 &#39;react&#39;;
import { useSearchParams } from &#39;react-router-dom&#39;;
import axios from &#39;axios&#39;;
import { serverUrl } from &#39;../serverUrl&#39;;
const ActivationPage = () =&gt; {
const [error, setError] = useState(false);
const [searchParams] = useSearchParams();
const activationToken = searchParams.get(&#39;token&#39;);
const wasAlreadyRequested = useRef(false);
useEffect(() =&gt; {
const activationEmail = async () =&gt; {
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 (
&lt;div className=&#39;w-full h-screen flex justify-center items-center&#39;&gt;
{error ? &lt;p&gt;Your token has expired!&lt;/p&gt; : &lt;p&gt;Your account has been activated successfully!&lt;/p&gt;}
&lt;/div&gt;
);
};
export default ActivationPage;

答案2

得分: 0

以下是翻译好的部分:

这是一个 useEffect 代码
使用这段代码
useEffect(() => {
first
return () => {
second
}
}, [third])
英文:

this is a the useeffect code
use this code

  useEffect(() =&gt; {
first
return () =&gt; {
second
}
}, [third])

huangapple
  • 本文由 发表于 2023年8月4日 23:23:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76837284.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定