React组件状态数据传递至Node.js服务器报错求助
问题描述
我尝试将React组件state中存储的emailUserInput数据发送至Node.js服务器的server.js文件,但服务器终端出现错误:
cannot read properties of undefined (reading 'emailUserInput')
同时客户端浏览器控制台显示请求因状态码500失败(ERR_BAD_RESPONSE),查看object.config发现数据已在data键中传递。请问是数据传递方式存在问题还是服务器本身的问题?
服务器端代码(server.js)
const express = require("express"); //needed to launch server const cors = require("cors"); //needed to disable sendgrid security const app = express(); //alias from the express function app.use(cors()); const hostname = "server"; const port = 4000; var corsOptions = { origin: 'http://localhost:3000', optionsSuccessStatus: 200 } app.post("/", cors(corsOptions), (req, res) => { res.setHeader("Access-Control-Allow-Origin", "*"); const emailInfo = req.body.emailUserInput; console.log(emailInfo); res.json(emailInfo); // sendgrid details // require("dotenv").config(); const sgMail = require("@sendgrid/mail"); const apikey = process.env.SENDGRID_API_KEY; sgMail.setApiKey(apikey); const msg = { to: emailInfo, from: "email", subject: "Congratulations! Your event link has been created!", text: `Here is your event link:`, html: "<strong>and easy to do anywhere, even with Node.js</strong>", }; // email sending logic // //ES8 (async () => { try { await sgMail.send(msg); } catch (error) { console.error(error); if (error.response) { console.error(error.response.body); } } })(); }); app.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); });
React组件代码
import "../StyleComponents/CreateEvent.css"; import { useState } from 'react'; import { db } from '../firebase'; import { uid } from "uid"; import { set, ref } from "firebase/database"; import Header from "./Header"; import { useNavigate } from 'react-router-dom'; import { getStorage, ref as sref, uploadBytes } from "firebase/storage"; import PlacesAutocomplete from 'react-places-autocomplete'; import { geocodeByAddress, getLatLng } from 'react-places-autocomplete'; import axios from 'axios'; function CreateEvent() { // store user inputs in state // const [titleUserInput, setTitleUserInput] = useState(''); const [dateUserInput, setDateUserInput] = useState(''); const [timeUserInput, setTimeUserInput] = useState(''); const [address, setAddress] = useState(''); const [setCoordinates] = useState({ lat: null, lng: null, }) const [headerUserInput, setHeaderUserInput] = useState(''); const [detailsUserInput, setDetailsUserInput] = useState(''); const [lengthUserInput, setLengthUserInput] = useState(''); const [emailUserInput, setEmailUserInput] = useState(''); const [userSubmit, setUserSubmit] = useState(''); const [error, setError] = useState(false); // Create a root reference for storing event photos // const storage = getStorage(); // handle changing user input data // const handleTitleChange = (e) => { setTitleUserInput(e.target.value); } const handleDateChange = (e) => { setDateUserInput(e.target.value); } const handleTimeChange = (e) => { setTimeUserInput(e.target.value); } const handleSelect = async (value) => { const results = await geocodeByAddress(value); const latLng = await getLatLng(results[0]); setAddress(value); setCoordinates(latLng); } const handleDetailsChange = (e) => { setDetailsUserInput(e.target.value); } const handleLengthChange = (e) => { setLengthUserInput(e.target.value); } const handleHeaderChange = (e) => { // Create a root reference for storing event photos // setHeaderUserInput(e.target.files[0]); } const handleEmailChange = (e) => { setEmailUserInput(e.target.value); } const navigate = useNavigate(); // make call to the backend database to send email user input data // const serverUrl = 'http://localhost:4000'; // submit user data to database with unique ID for each event // const writeToDataBase = () => { let uuid = uid() if (titleUserInput.length === 0 || dateUserInput.length === 0 || timeUserInput.length === 0 || address.length === 0 || emailUserInput.length === 0) { setError(true); } if (titleUserInput && dateUserInput && timeUserInput && address && emailUserInput) { const storageRef = sref(storage, uuid); set(ref(db, `/${uuid}`), { EventPhoto: headerUserInput, EventTitle: titleUserInput, EventDate: dateUserInput, EventTime: timeUserInput, EventLength: lengthUserInput, EventLocation: address, EventDetails: detailsUserInput, }); const getEmailInput = () => { axios.post(serverUrl, emailUserInput).then((res) => { console.log(res); }).catch(console.log('error')); } getEmailInput(''); setUserSubmit(''); uploadBytes(storageRef, headerUserInput).then(() => { navigate(`/EventCreated/${uuid}`); }); } } return ( <> < Header /> <div className="event-creation-container"> <h1>Create a New Event</h1> <form> <div className="event-name-container event-input"> <label for="eventTitle">Name of Event<span>*</span></label> <input type="text" id="EventTitle" value={titleUserInput} onChange={handleTitleChange} /> {error && titleUserInput === '' ? <label id="form-validation-label">Event name must be filled</label> : ""} </div> <div className="date-time-length"> <div className="date-input-container event-input"> <label for="Date">Date<span>*</span></label> <input type="date" id="EventDate" value={dateUserInput} onChange={handleDateChange} /> {error && dateUserInput === '' ? <label id="form-validation-label">Event date must be filled</label>: ""} </div> <div className="time-input-container event-input"> <label for="Time">Time<span>*</span></label> <input id="EventTime" type="time" name="time" timezone="timezone" value={timeUserInput} onChange={handleTimeChange} /> </div> {error && timeUserInput === '' ? <label id="form-validation-label">Event time must be filled</label> : ""} <div className="length-input-container event-input"> <label for="Length">Length</label> <input id="EventLength" type="text" value={lengthUserInput} onChange={handleLengthChange} /> </div> </div> <div className="location-input-container event-input"> <label for="Location">Location<span>*</span></label> <PlacesAutocomplete onChange={setAddress} value={address} onSelect={handleSelect} > {({ getInputProps, suggestions, getSuggestionItemProps, loading }) => ( <div> <input id="EventLocation" {...getInputProps()} /> <div className="location-suggestions"> {loading ? <div>...loading</div> : null} {suggestions.map((suggestion) => { const style = { backgroundColor: suggestion.active ? "#41b6e6" : "#fff" }; return <div {...getSuggestionItemProps(suggestion, { style })}>{suggestion.description}</div> })} </div> </div> )} </PlacesAutocomplete> </div> {error && address ==='' ? <label id="form-validation-label">Event location must be filled</label> : ""} <div className="details-input-container event-input"> <label for="Event_Details">Event Details</label> <textarea type="text" id="EventDetails" value={detailsUserInput} onChange={handleDetailsChange} /> </div> <div className="header-input-container event-input"> <div className="upload-image-flex-container"> <label for="header_image">Upload Header Image (optional)</label> <input className="upload-input" type="file" id=" EventImage" name="filename" accept="image/png, image/jpeg" onChange={handleHeaderChange} /> </div> </div> <div className="orangizer-email-container"> <label for="organizer-email">Organizer's Email<span>*</span></label> <p>The event page link will be sent to your email</p> <input id="EventEmail" type="email" name="email" value={emailUserInput} onChange={handleEmailChange} /> {error && emailUserInput === '' ? <label id="form-validation-label">Event organizer's email must be entered</label> : ""} </div> <div className="create-event-btn-container"> <button className="event-create-button" type="button" value={userSubmit} onClick={writeToDataBase}>Create Event</button> </div> </form> </div> </> )} export default CreateEvent;
问题原因及解决方法
1. 服务器端核心问题:缺少JSON请求体解析中间件
Express默认不会自动解析JSON格式的请求体,导致req.body为undefined,读取emailUserInput时触发报错。
解决步骤:
在app.use(cors())之后添加以下代码:
app.use(express.json());
2. 客户端数据格式不匹配
当前React代码中,axios.post直接发送字符串类型的emailUserInput,但服务器期望从req.body.emailUserInput读取属性值,需要将请求数据包装为对象。
解决步骤:
修改React中的axios.post调用:
axios.post(serverUrl, { emailUserInput: emailUserInput }) // 或使用ES6简写: axios.post(serverUrl, { emailUserInput })
3. 可选优化点
- 将
require("dotenv").config()和const sgMail = require("@sendgrid/mail")移到文件顶部,避免在每个请求中重复加载模块。 - 调整邮件发送逻辑的位置,确保响应客户端前完成邮件发送(或使用
await处理异步操作),避免提前响应导致的潜在问题。
内容的提问来源于stack exchange,提问作者DannyMac
相关产品推荐
相关产品推荐

