React多页表单EmailJS发送数据问题:如何跨组件获取表单数据
React多页表单数据收集与EmailJS发送解决方案
问题原因
你当前遇到的空邮件问题,核心原因有两个:
emailjs.sendForm依赖当前DOM中存在的表单输入元素,但多页表单切换时,非当前页面的组件会被卸载,对应的输入框DOM不存在,导致EmailJS无法获取这些页面的输入值。- 子组件各自维护状态并存在sessionStorage,但父组件
Form没有统一收集所有页面的数据,提交时无法获取完整表单内容。
解决方案
方案一:状态提升到父组件(推荐,符合React数据流规范)
将所有表单数据的状态统一放在父组件Form中,子组件通过props接收值和更新方法,父组件可随时获取完整数据,提交时直接使用这些数据发送邮件。
1. 修改Form组件
import styled from "styled-components" import { useState } from "react"; // 导入子组件... import emailjs from '@emailjs/browser'; import ConfettiExplosion from 'react-confetti-explosion'; function Form() { const [page, setPage] = useState(0); const [isExploding, setIsExploding] = useState(false); const FormTitles = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; const confettiProps = { force: 0.9, duration: 4000, particleCount: 500, width: 2000, height: '200vh', } // 初始化表单数据,从sessionStorage读取已保存的值 const [formData, setFormData] = useState({ user_name: JSON.parse(sessionStorage.getItem('FirstAndLastName') || '""'), user_email: JSON.parse(sessionStorage.getItem('email') || '""'), user_companyName: JSON.parse(sessionStorage.getItem('companyName') || '""'), user_companyIndustry: JSON.parse(sessionStorage.getItem('companyIndustry') || '""'), // 其他子组件的字段需在此处补充 }); // 更新表单数据的统一方法 const updateFormData = (fieldName, value) => { const newData = { ...formData, [fieldName]: value }; setFormData(newData); sessionStorage.setItem(fieldName, JSON.stringify(value)); // 同步到sessionStorage }; const sendEmail = (e) => { e.preventDefault(); // 使用send方法代替sendForm,直接传入父组件维护的完整数据 emailjs.send( import.meta.env.VITE_EMAILJS_SERVICE_ID, import.meta.env.VITE_EMAILJS_TEMPLATE_ID, formData, import.meta.env.VITE_EMAILJS_PUBLIC_KEY ) .then((result) => { console.log(result.text); console.log('message sent'); setIsExploding(true); }, (error) => { console.log(error.text); }); }; const handleIncrementClick = () => setPage(prev => prev + 1); const handleDecrementClick = () => setPage(prev => prev - 1); const pageDisplay = () => { switch(page) { case 0: return <Welcome/>; case 1: return <PersonalInfo formData={formData} updateFormData={updateFormData} />; // 其他子组件需同样传递formData和updateFormData props case 2: return <BackgroundInfo formData={formData} updateFormData={updateFormData}/>; // ...其他页面组件 case 13: return <Submit/>; default: return null; } } return ( <FormWrapperOuter onSubmit={sendEmail}> <ProgressBarContainer> <ProgressBar style={{width: `${page * 8}%`}}></ProgressBar> </ProgressBarContainer> <FormWrapperInner> <Body>{pageDisplay()}</Body> <ButtonContainer> { page > 0 && <PrevButton disabled={page === 0} type="button" onClick={handleDecrementClick}><i className="fa-solid fa-arrow-left-long"></i></PrevButton>} {page < FormTitles.length - 1 && <NextButton disabled={page === FormTitles.length - 1} type="button" onClick={handleIncrementClick}>{page > 0 ? <p>Next</p> : <p>Let's go!</p>}<i className="fa-solid fa-arrow-right-long"></i></NextButton>} <SubmitBtn type="submit"><p>Submit</p><i className="fa-solid fa-paper-plane"></i></SubmitBtn> {isExploding && <ConfettiExplosion {...confettiProps}/>} </ButtonContainer> </FormWrapperInner> </FormWrapperOuter> ) }
2. 修改PersonalInfo组件
移除子组件内部的状态,改用父组件传递的props:
import React from 'react' import styled from 'styled-components' function PersonalInfo({ formData, updateFormData }) { return ( <PersonalInfoContainer> <InputContainer> <Input name="user_name" type='text' value={formData.user_name} onChange={(e) => updateFormData('user_name', e.target.value)} placeholder='' /> <Label>First and Last Name</Label> </InputContainer> <InputContainer> <Input name='user_email' type='email' value={formData.user_email} onChange={(e) => updateFormData('user_email', e.target.value)} placeholder='' /> <Label>Email</Label> </InputContainer> <InputContainer> <Input name='user_companyName' type='text' value={formData.user_companyName} onChange={(e) => updateFormData('user_companyName', e.target.value)} placeholder='' /> <Label>Company Name</Label> </InputContainer> <InputContainer> <Input name='user_companyIndustry' type='text' value={formData.user_companyIndustry} onChange={(e) => updateFormData('user_companyIndustry', e.target.value)} placeholder='' /> <Label>Company Industry</Label> </InputContainer> </PersonalInfoContainer> ) }
方案二:从sessionStorage统一读取数据提交
如果不想修改子组件的状态管理,可在提交时从sessionStorage中读取所有表单字段,组装成EmailJS需要的数据结构后发送:
// 修改Form组件的sendEmail方法 const sendEmail = (e) => { e.preventDefault(); // 从sessionStorage读取所有表单数据 const formData = { user_name: JSON.parse(sessionStorage.getItem('FirstAndLastName') || '""'), user_email: JSON.parse(sessionStorage.getItem('email') || '""'), user_companyName: JSON.parse(sessionStorage.getItem('companyName') || '""'), user_companyIndustry: JSON.parse(sessionStorage.getItem('companyIndustry') || '""'), // 其他子组件的字段需在此处补充 }; emailjs.send( import.meta.env.VITE_EMAILJS_SERVICE_ID, import.meta.env.VITE_EMAILJS_TEMPLATE_ID, formData, import.meta.env.VITE_EMAILJS_PUBLIC_KEY ) .then((result) => { console.log(result.text); console.log('message sent'); setIsExploding(true); }, (error) => { console.log(error.text); }); };
关键注意事项
- 确保EmailJS模板中的变量名与
formData的键名完全一致(比如模板用{{user_name}},则formData需包含user_name字段)。 - 方案一的状态提升更便于后续维护和扩展,符合React单向数据流设计原则,优先推荐。
内容的提问来源于stack exchange,提问作者Christoper Chamberlain
相关产品推荐
相关产品推荐

