React+Axios+Express表单提交失败及未使用变量警告修复
Let's break down each problem and fix them step by step to get your contact form sending emails properly:
1. Fix Form Submission Default Behavior (Page Refresh Issue)
The core reason your page refreshes and appends form data to the URL is that you attached the onSubmit handler to a FormGroup instead of the root Form component. Browsers trigger the default GET form submission when the submit button is clicked if the onSubmit isn't bound to the actual form element—so your e.preventDefault() was never even running!
Fix: Move the onSubmit prop to the Form component, and correct the event type in handleSubmit (it should be React.FormEvent<HTMLFormElement>, not HTMLInputElement).
2. Fix State Updates in handleChange
Right now, your handleChange sets both email and message state values to the same input value. That means whichever field you type in last will overwrite the other. Instead, use the input's name attribute to target the correct state field.
3. Fix Axios Request Structure & Unused Variable Warning
You're wrapping email and message in extra objects when sending the POST request, which makes the backend receive nested data (like req.body.email.email instead of req.body.email). Also, the form variable was assigned but never used—either remove the assignment or use it to handle the response (like logging success).
4. Backend Improvements
- Access form data correctly once the frontend sends it properly
- Send a response back to the frontend after processing the email (otherwise the request will hang)
- Use async/await for cleaner, more readable nodemailer code
Corrected Frontend App.tsx
import React, { Component } from "react"; import { Button, Form, FormGroup, Label, Input } from "reactstrap"; import axios from "axios"; class App extends Component { state = { email: "", message: "" }; handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { // Use input's name attribute to update the correct state field this.setState({ [e.target.name]: e.target.value } as Pick<typeof this.state, string>); }; async handleSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); // Now works correctly since it's bound to the form try { // Send state directly without extra nesting const response = await axios.post("/api/form", { email: this.state.email, message: this.state.message, }); console.log("Email sent successfully:", response.data); // Reset form after success this.setState({ email: "", message: "" }); } catch (error) { console.error("Error sending email:", error); } } render() { return ( <> <div> {/* Move onSubmit to the Form component */} <Form id="contact-form" onSubmit={this.handleSubmit} method="POST"> <FormGroup> <Label for="email">Email</Label> {/* Add name attribute matching state key */} <Input type="email" name="email" id="email" placeholder="with a placeholder" value={this.state.email} onChange={this.handleChange} /> </FormGroup> <FormGroup> <Label for="message">Message</Label> {/* Add name attribute matching state key */} <Input type="textarea" name="message" id="message" placeholder="with a placeholder" value={this.state.message} onChange={this.handleChange} /> </FormGroup> <Button type="submit">Submit</Button> </Form> </div> </> ); } } export default App;
Corrected Backend index.ts
import express, { Application, Request, Response } from "express"; import bodyParser from "body-parser"; import nodemailer from "nodemailer"; const app: Application = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.post("/api/form", async (req: Request, res: Response) => { try { // Initialize transporter with async/await const transporter = nodemailer.createTransport({ host: "smtp.ethereal.email", port: 587, auth: { user: "MY_TEST_EMAIL_NAME", // Replace with your Ethereal username pass: "MY_TEST_EMAIL_PASSWORD", // Replace with your Ethereal password }, }); const htmlEmail = ` <h3>Contact details</h3> <ul> <li>Email: ${req.body.email}</li> </ul> <h3>Message</h3> <p>${req.body.message}</p> `; const mailOptions = { from: "test@testaccount.com", to: "MY_TEST_EMAIL_NAME", replyTo: req.body.email, // Use user's email for replies subject: "New Message from Contact Form", text: req.body.message, html: htmlEmail, }; const info = await transporter.sendMail(mailOptions); console.log("Message sent: %s", info.messageId); console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info)); // Send success response to frontend res.status(200).json({ success: true, message: "Email sent successfully" }); } catch (err) { console.error("Error sending email:", err); res.status(500).json({ success: false, error: "Failed to send email" }); } }); const PORT = 5000; app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });
Quick Additional Checks
- Add
"proxy": "http://localhost:5000"to your frontend'spackage.json—this lets Axios forward/api/formrequests to your backend without needing the full URL. - Ethereal emails are for testing only: you won't get real emails in your inbox, but use the preview URL logged in the backend terminal to see what the email looks like. For production, switch to a real SMTP service like SendGrid or Mailgun.
内容的提问来源于stack exchange,提问作者user11788161

