Search code examples
reactjsbraintree

Doble creation of braintree customer ID when create an account


enter image description here

I'm working in a React project, so every time I sign up a new user, it's created 2 customer ID, it's supposed to create inly one per user.

This is my code:

      const ConfirmEmail = ({ handleItems, onSubmitUser }) => {
      let params = useParams();
      
      let isMounted = true;
      const [loading, setLoading] = useState(true);
      
      useCallback(() => {
        fetchData();
      }, [isMounted]);
      
      const fetchData = async () => {
        try {
          if (JSON.parse(localStorage.getItem("user"))) return;
          const response = await UserService.confirmEmail(params.tokenEmail);
          if (response && response.data && !response.err) {
            const { user, accessToken } = response.data;
            localStorage.setItem("accessToken", accessToken);
            if (isMounted) {
              handleItems();
              onSubmitUser(user);
            }
          }
          if (isMounted) setLoading(false);
        } catch (err) {
          const { response } = err;
          if (response?.data?.data) toast.error(response.data.data);
        }
    
        // Unmounted
    
        return (isMounted = false);
      };

Solution

  • You probably made a mistake when writing the code in the question, and you actually have:

    useEffect(() => {
      fetchData();
    }, []);
    

    Rather than:

    useCallback(() => {
      fetchData();
    }, [])
    

    As in the second option, the callback you define is never called.

    I suspect you are only experiencing this issue in development, because React calls useEffect twice in development when using React.StrinctMode (quick way to verify, just remove React.StrictMode).

    You can fix this using a ref to make sure you only send one request:

    const ConfirmEmail = ({
      handleItems,
      onSubmitUser,
    }) => {
      const params = useParams();
    
      const [loading, setLoading] = useState(true);
    
      const confirmEmail = useCallback(() => {        
        try {
          if (localStorage.getItem("user")) return;
    
          const response = await UserService.confirmEmail(params.tokenEmail);      
    
          if (response && response.data) {
            const { user, accessToken } = response.data;
    
            localStorage.setItem("accessToken", accessToken);
    
            handleItems();
            onSubmitUser(user);
          }
        } catch (err) {
          toast.error(err.message || 'Something went wrong.');
        }
    
        setLoading(false);      
      }, [params, handleItems, onSubmitUser])
    
      const emailConfirmed = useRef(false)
    
      useEffect(() => {
        if (emailConfirmed.current) return;
    
        emailConfirmed.current = true;
    
        // This will only be called once when the component is mounted:
        confirmEmail();
      }, [confirmEmail]);
    };