Home > Back-end >  Cannot send email with EmailJS/React
Cannot send email with EmailJS/React

Time:05-05

I've been working through a MERN stack tutorial, and I thought I'd challenge myself by using EmailJS. What I'm trying to do is trigger EmailJS to send a confirmation email when a new user creates an account. I'm using the correct EmailJS library, and have tried and tried and tried using their React example from their docs, which is below:

import emailjs from '@emailjs/browser';

export const ContactUs = () => {
  const form = useRef();

  const sendEmail = (e) => {
    e.preventDefault();

    emailjs.sendForm('YOUR_SERVICE_ID', 'YOUR_TEMPLATE_ID', form.current, 'YOUR_PUBLIC_KEY')
      .then((result) => {
          console.log(result.text);
      }, (error) => {
          console.log(error.text);
      });
  };

  return (
    <form ref={form} onSubmit={sendEmail}>
      <label>Name</label>
      <input type="text" name="user_name" />
      <label>Email</label>
      <input type="email" name="user_email" />
      <label>Message</label>
      <textarea name="message" />
      <input type="submit" value="Send" />
    </form>
  );
};

This tutorial uses context and reducers—so I'm not sure whether this should go into the context or not (still learning context and reducers!). This is my onSubmit function within the form component—I removed the EmailJS code for the sake of getting my app running, but it's structured identically to the example, so const form = useRef() is being declared within onSubmit.

const onSubmit = (e) => {
    e.preventDefault();
    const { name, email, password, isMember } = values;
    if (!email || !password) {
      displayAlert();
      return;
    }
    const currentUser = { name, email, password };
    if (isMember) {
      setupUser({
        currentUser,
        endPoint: 'login',
        alertText: 'Login Successful! Redirecting...',
      });
    } else {
      setupUser({
        currentUser,
        endPoint: 'register',
        alertText: 'User Created! Redirecting...',
      });
    }
  };

I've been putting the EmailJS example after calling setupUser in onSubmit within the else block. I've also structured my form tag just like in the example. The one difference is that in the example, their onSubmit function is sendEmail and mine will be shared below. According to the console, I'm getting an 'OK' as a response rather than an error—but no email is being sent. I've of course checked my EmailJS credentials and am not putting them in a .env just yet for testing purposes. My setupUser function within my context file is:

const setupUser = async ({ currentUser, endPoint, alertText }) => {
    dispatch({ type: SETUP_USER_BEGIN })
    try {
      const { data } = await axios.post(`/api/v1/auth/${endPoint}`, currentUser)

      const { user, token } = data
      dispatch({
        type: SETUP_USER_SUCCESS,
        payload: { user, token, alertText },
      })
      addUserToLocalStorage({ user, token })
    } catch (error) {
      dispatch({
        type: SETUP_USER_ERROR,
        payload: { msg: error.response.data.msg },
      })
    }
    clearAlert()
  }

I haven't tried putting anything EmailJS-related into the context file because I just got to a point where I was spinning my wheels. My app also has an Express backend, so I thought about using their REST API, but again—was spinning my wheels and wanted to take a break.

Does anyone have any ideas or suggestions? I can provide more of my code if needed—I held back because it was lengthy, and I hope I provided enough information.

CodePudding user response:

You will need to learn more about handling forms in React.

The emailJS library uses ref just to get the information from the form. That's it. What it means is even if you don't have a form, you can pass the information directly into the emailJS lib itself (if you want)

So you can start by learning how to handle a form in React. Purely react. Up until the point where you can get all the information you want in your submitHandler.
From this point, you have all the infomration you need to pass down to emailJS, you can think of it just a function: EmailJS(your-form-info). Of course, emailJS doesn't work that way but in it heart, it's just a function like that.

And there's no backend involve here, all you do is handle the form which is react, and emailjs is a service, which helps you send email, so your mentioned express is irrelevant here.

CodePudding user response:

ETA: If necessary, I can share my context file and the <form> part of my code.

I finally figured this out and wanted to post my code just in case anyone runs into this same problem and hopefully you can avoid some of my mistakes. First and foremost, the beginning of my problems started within EmailJS itself. I use their templates, and my variables weren't matching my updated code.

I was unable to get ref to work because my form was never structured like that. I've used ref many times in the past successfully. I also have a handleSubmit function tied to my form component. I'm not sure whether that was part of the problem with ref since I was already identifying values in the form—value={values.email}, etc. Anyway—this might be a roundabout way of doing this, but it's been consistently working.

So first, I went ahead and got my variables in EmailJS to match up with my code. This is my full code from my Register component (minus the return and also needs some code cleanup):

import { useState, useEffect } from 'react';
import { Logo, FormRow, Alert } from '../components';
import Wrapper from '../assets/wrappers/RegisterPage';
import { useAppContext } from '../context/appContext';
import { useNavigate } from 'react-router-dom';
import emailjs from '@emailjs/browser';

const initialState = {
  name: '',
  email: '',
  password: '',
  isMember: true,
};

const Register = () => {
  const navigate = useNavigate();
  const [values, setValues] = useState(initialState);
  const { user, isLoading, showAlert, displayAlert, setupUser } =
    useAppContext();
  
  const obj = {
    userName: values.name,
    userEmail: values.email,
  };
  
  const sendNewAccountEmail = (obj) => {
    emailjs
      .send(
        'service id goes here',
        'template id goes here',
        obj,
        'public key goes here'
      )
      .then(
        (result) => {
          console.log(result.text);
        },
        (error) => {
          console.log(error.text);
        }
      );
  };

  const toggleMember = () => {
    setValues({ ...values, isMember: !values.isMember });
  };

  const handleChange = (e) => {
    setValues({ ...values, [e.target.name]: e.target.value });
  };
  const onSubmit = (e) => {
    e.preventDefault();
    const { name, email, password, isMember } = values;
    if (!email || !password) {
      displayAlert();
      return;
    }
    const currentUser = { name, email, password };
    if (isMember) {
      setupUser({
        currentUser,
        endPoint: 'login',
        alertText: 'Login Successful! Redirecting...',
      });
    } else {
      setupUser({
        currentUser,
        endPoint: 'register',
        alertText: 'User Created! Redirecting...',
      });
      sendNewAccountEmail();
    }
  };

  useEffect(() => {
    if (user) {
      setTimeout(() => {
        navigate('/');
      }, 3000);
    }
  }, [user, navigate]);

More mistakes! I wasn't using my form values correctly in my object—for example, I had

  const obj = {
    userName: name,
    userEmail: email,
  };

That threw an error because I wasn't supposed to use name, but the more I looked at it, I knew it was wrong. I then tried using

  const obj = {
    userName: user.name,
    userEmail: user.email,
  };

Silly me—I never defined user. On top of that—I never set up the actual object yesterday!

Another mistake was trying to write the sendNewAccountEmail within the onSubmit function. I'm pretty sure that I structured it wrong—so perhaps that part is possible.

Finally, my last mistake was calling sendNewAccountEmail(); after the if/else statement. That got me feeling hopeful because it worked when I created a new user! BUT—in my code, you'll see that I'm using the setupUser function twice as it (for the most part) does the same thing when a user logs in—it just goes to different endPoints. And guess what happened—emails were sent at registration and login. Once I moved it into the else block and called it after setupUser, it finally worked as it should!

If you made it this far, I really appreciate it, and I hope that it made sense can can be useful to others. I'm still working on my coding skills (lots to improve) and learning a lot as I go, so thanks for 1. reading this and 2. understanding my current skillset. :) Also if anything in my working code looks weird—I'd totally welcome feedback if it's relevant.

And shoutout to Jimmy in the comments for leading me in the right direction.

  • Related