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

Antd v4多步骤表单数据丢失求助:提交时仅获最后一步数据

Fixing Multi-Step Form Data Loss with Ant Design's useForm

Hey there! I get that you're building a multi-step form with React and Ant Design using the useForm hook, and you're stuck on the issue where only the last step's data comes through when you submit. The root cause is exactly what you guessed—when you switch steps, the previous step's DOM gets unmounted, so those form fields are no longer tracked by the Form instance. Let's walk through two simple, no-third-party-plugin solutions to fix this.

1. Keep All Form Fields Mounted (Toggle Visibility Only)

This is the simplest approach. Instead of unmounting previous steps entirely, just hide them with CSS. Since all fields stay attached to the Form instance, their values will be preserved and collected on submit.

Example Code

import { useState } from 'react';
import { Form, Steps, Button, Input, Select } from 'antd';

const { Step } = Steps;

const Step1Form = () => (
  <>
    <Form.Item label="First Name" name="firstName" rules={[{ required: true, message: 'Please enter your first name' }]}>
      <Input />
    </Form.Item>
    <Form.Item label="Last Name" name="lastName" rules={[{ required: true, message: 'Please enter your last name' }]}>
      <Input />
    </Form.Item>
  </>
);

const Step2Form = () => (
  <>
    <Form.Item label="Email" name="email" rules={[{ required: true, type: 'email', message: 'Please enter a valid email' }]}>
      <Input />
    </Form.Item>
    <Form.Item label="Phone" name="phone" rules={[{ required: true, message: 'Please enter your phone number' }]}>
      <Input />
    </Form.Item>
  </>
);

const Step3Form = () => (
  <>
    <Form.Item label="Address" name="address" rules={[{ required: true, message: 'Please enter your address' }]}>
      <Input.TextArea />
    </Form.Item>
    <Form.Item label="Country" name="country" rules={[{ required: true, message: 'Please select your country' }]}>
      <Select>
        <Select.Option value="US">United States</Select.Option>
        <Select.Option value="CA">Canada</Select.Option>
        <Select.Option value="UK">United Kingdom</Select.Option>
      </Select>
    </Form.Item>
  </>
);

const MultiStepForm = () => {
  const [currentStep, setCurrentStep] = useState(0);
  const form = Form.useForm();

  const steps = [
    { title: 'Personal Info', content: <Step1Form /> },
    { title: 'Contact Info', content: <Step2Form /> },
    { title: 'Address Details', content: <Step3Form /> },
  ];

  const handleSubmit = () => {
    form.validateFields()
      .then(allValues => {
        console.log('All Form Data:', allValues);
        // Add your submission logic here
      })
      .catch(errorInfo => {
        console.log('Validation Failed:', errorInfo);
      });
  };

  return (
    <Form form={form} layout="vertical" style={{ maxWidth: 600, margin: '0 auto' }}>
      <Steps current={currentStep}>
        {steps.map(step => (
          <Step key={step.title} title={step.title} />
        ))}
      </Steps>

      <div className="step-content" style={{ margin: '24px 0' }}>
        {steps.map((step, index) => (
          <div key={index} style={{ display: currentStep === index ? 'block' : 'none' }}>
            {step.content}
          </div>
        ))}
      </div>

      <div className="step-actions">
        {currentStep > 0 && (
          <Button onClick={() => setCurrentStep(currentStep - 1)} style={{ marginRight: 8 }}>
            Previous
          </Button>
        )}
        {currentStep < steps.length - 1 ? (
          <Button type="primary" onClick={() => setCurrentStep(currentStep + 1)}>
            Next
          </Button>
        ) : (
          <Button type="primary" onClick={handleSubmit}>
            Submit
          </Button>
        )}
      </div>
    </Form>
  );
};

export default MultiStepForm;

How It Works

  • All step components remain mounted in the DOM, but we use display: none to hide the inactive ones.
  • The Form instance tracks all fields across every step, so when you submit, it collects values from all mounted fields automatically.
  • No extra state management needed—this leverages Ant Design Form's built-in field tracking.

2. Save Step Data to State on Navigation

If you prefer to unmount inactive steps (for performance reasons with very complex forms), you can save each step's data to a state variable when navigating, then merge all data on submit.

Example Code

import { useState, useEffect } from 'react';
import { Form, Steps, Button, Input, Select } from 'antd';

const { Step } = Steps;

// Step components remain the same as above...

const MultiStepForm = () => {
  const [currentStep, setCurrentStep] = useState(0);
  const [savedStepData, setSavedStepData] = useState({});
  const form = Form.useForm();

  const steps = [
    { title: 'Personal Info', content: <Step1Form /> },
    { title: 'Contact Info', content: <Step2Form /> },
    { title: 'Address Details', content: <Step3Form /> },
  ];

  const handleNext = () => {
    form.validateFields()
      .then(currentValues => {
        // Save current step's data to state
        setSavedStepData(prev => ({ ...prev, ...currentValues }));
        setCurrentStep(currentStep + 1);
      })
      .catch(error => {
        console.log('Current Step Validation Failed:', error);
      });
  };

  const handlePrevious = () => {
    // Optional: Save current step data before going back
    form.validateFields()
      .then(currentValues => {
        setSavedStepData(prev => ({ ...prev, ...currentValues }));
        setCurrentStep(currentStep - 1);
      })
      .catch(() => {
        // Allow navigating back even if validation fails (adjust as needed)
        setCurrentStep(currentStep - 1);
      });
  };

  const handleSubmit = () => {
    form.validateFields()
      .then(finalStepValues => {
        // Merge saved data with final step data
        const allFormData = { ...savedStepData, ...finalStepValues };
        console.log('All Form Data:', allFormData);
        // Add your submission logic here
      })
      .catch(errorInfo => {
        console.log('Final Validation Failed:', errorInfo);
      });
  };

  // Populate form with saved data when navigating back to a step
  useEffect(() => {
    form.setFieldsValue(savedStepData);
  }, [currentStep, savedStepData, form]);

  return (
    <Form form={form} layout="vertical" style={{ maxWidth: 600, margin: '0 auto' }}>
      <Steps current={currentStep}>
        {steps.map(step => (
          <Step key={step.title} title={step.title} />
        ))}
      </Steps>

      <div className="step-content" style={{ margin: '24px 0' }}>
        {/* Only render the active step */}
        {steps[currentStep].content}
      </div>

      <div className="step-actions">
        {currentStep > 0 && (
          <Button onClick={handlePrevious} style={{ marginRight: 8 }}>
            Previous
          </Button>
        )}
        {currentStep < steps.length - 1 ? (
          <Button type="primary" onClick={handleNext}>
            Next
          </Button>
        ) : (
          <Button type="primary" onClick={handleSubmit}>
            Submit
          </Button>
        )}
      </div>
    </Form>
  );
};

export default MultiStepForm;

How It Works

  • When you click "Next", we validate the current step's fields, save their values to savedStepData, then move to the next step.
  • When navigating back, we populate the form with the saved data so the user doesn't lose their progress.
  • On submit, we merge the saved data with the final step's values to get the complete form dataset.

Recommendation

For most cases, Method 1 is the best choice—it's simpler, requires less code, and leverages Ant Design's Form system as intended. Only use Method 2 if you have performance concerns with mounting many complex components at once.

内容的提问来源于stack exchange,提问作者Qadeer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 08:22:50