You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React多页表单EmailJS发送数据问题:如何跨组件获取表单数据

React多页表单数据收集与EmailJS发送解决方案

问题原因

你当前遇到的空邮件问题,核心原因有两个:

  1. emailjs.sendForm依赖当前DOM中存在的表单输入元素,但多页表单切换时,非当前页面的组件会被卸载,对应的输入框DOM不存在,导致EmailJS无法获取这些页面的输入值。
  2. 子组件各自维护状态并存在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:03:11