Search code examples
node.jsreactjsexpressnodemailer

Node.js/React Form submission


So I've built a contact form on my site and my form wont submit. both ports 5000 and 8080 wont work. I'm using Node.js, Express, nodemailer, and cors. I have tried various things already, such as different ports, making sure ports are open, changing local host details moving files in file tree etc. I am totally at a loss.

Here's the code:

Contact form:

    import React, { useState} from 'react'
// import "./contact.scss"


const ContactForm = () => {
    
    const [status, setStatus] = useState("Submit");
    const handleSubmit = async (e) => {
      e.preventDefault();
      setStatus("Sending...");
      const { name, email, message } = e.target.elements;
      let details = {
        name: name.value,
        email: email.value,
        message: message.value,
      };
      let response = await fetch("http://localhost:8080/contact", {
        method: "POST",
        headers: {
          "Content-Type": "application/json;charset=utf-8",
        },
        body: JSON.stringify(details),
      });
      setStatus("Submit");
      let result = await response.json();
      alert(result.status);
    };
    return (
        <div className="right">
          <div className="rightcontainer">
            <h3>Your Information Below</h3>
            <form onSubmit={handleSubmit}>
            <div>
              <label htmlFor="name">Name:</label>
              <input
              style={
                {width: '100%', 
                height: '40px', 
                margin: '10px'} 
                } className='name' type="text" id="name" placeholder="Name" required />
            </div>
            <div>
              <label htmlFor="email">Email:</label>
              <input
              style={
                {width: '100%', 
                height: '40px', 
                margin: '10px'} 
                } className='email' type="email" id="email" placeholder="Email" required />
            </div>
            <div>
              <label htmlFor="message">Message:</label>
              <textarea 
              style={
                {width: '100%', 
                height: '100px', 
                margin: '10px',
                } 
                } id="message" placeholder="message" required />
            </div>
            <button
            style={
              {width: '100%', 
              height: '40px', 
              margin: '10px'} 
              } type="submit" className='button'>{status}</button>
            </form>
          </div>
        </div>
      );
}

export default ContactForm

Server.js file:

const express = require("express");
const router = express.Router();
const cors = require("cors");
const nodemailer = require("nodemailer");

const app = express();
app.use(cors());
app.use(express.json());
app.use("/", router);
app.listen(8080, () => console.log("Server Running"));

const contactEmail = nodemailer.createTransport({
    service: 'gmail',
    auth: {
      user: "************@gmail.com",
      pass: "************",
    },
  });
  
  contactEmail.verify((error) => {
    if (error) {
      console.log(error);
    } else {
      console.log("Ready to Send");
    }
  });

  router.post("/contact", (req, res) => {
    const name = req.body.name;
    const email = req.body.email;
    const message = req.body.message; 
    const mail = {
      from: name,
      to: "************@gmail.com",
      subject: "Contact Form Submission",
      html: `<p>Name: ${name}</p>
             <p>Email: ${email}</p>
             <p>Message: ${message}</p>`,
    };
    contactEmail.sendMail(mail, (error) => {
      if (error) {
        res.json({ status: "ERROR" });
      } else {
        res.json({ status: "Message Sent" });
      }
    });
  });

Contact Page:

import "./contact.scss"
import ContactForm from "../../ContactForm";
import {Mail, GitHub, LinkedIn} from '@material-ui/icons/';
import React from 'react';


export default function Contact() {
  return (
    <div className='contact' id="contact">
      <div className="left">
        <div className="leftContainer">
            <div className="imgContainer">
                <Mail className="people"/>
            </div>
            <h2>Contact Me</h2>
            <p>
                If you would like to hire me on a contractual basis or hire me on a per project basis please use the form, alternatively contact me through:
            </p>
            <div className="icons">
              <a href="https://www.linkedin.com/in/jake-gliddon-5199481b7/"><LinkedIn className="icon"/></a>
              <a href="https://github.com/jake-gliddon"><GitHub className="icon" /></a>
            </div>
          </div>
      </div>
      <div className="right">
        <div className="rightContainer mobile">
          <ContactForm />
        </div>
      </div>
      </div>

  )
}

any help would be greatly appreciated!


Solution

  • Ok, so I think your problem is in your /contact route in the backend. In the router callback function, you are trying to access the properties name, email, and message of a string, not an object. In your ContactForm component you have this line: body: JSON.stringify(details),. This means in the backend you need to parse this JSON into a readable javascript object, like this: JSON.parse(req.body). Here's the full code:

      router.post("/contact", (req, res) => {
        // short for Parsed Body
        const pB = JSON.parse(req.body);
        const name = pB.name;
        const email = pB.email;
        const message = pB.message; 
        const mail = {
          from: name,
          to: "************@gmail.com",
          subject: "Contact Form Submission",
          html: `<p>Name: ${name}</p>
                 <p>Email: ${email}</p>
                 <p>Message: ${message}</p>`,
        };
        contactEmail.sendMail(mail, (error) => {
          if (error) {
            res.json({ status: "ERROR" });
          } else {
            res.json({ status: "Message Sent" });
          }
        });
      });
    

    Also if you would like a simpler way of making a POST request from the client I would recommend using the library axios.

    As for your file structure it should look like this:

    enter image description here

    Then you should have two terminal windows open. In one you start your react app:

    /client $ npm start

    And in another you start your server

    /server $ node server.js

    They should both be separately initated by npm, so create a server directory, then move your server.js file there, and install your dependencies. same with the client directory.

    Also, I would recommend using environment variables for your email details on a public repository. Try using dotenv in your server.

    When I tried running the server.js file, I got an invalid login error. This may just be on my end but it might be the next problem you need to solve. I also had to switch the port from 5000 to 8080, if you are working on a mac there might be some type of service running on this port.