Antd v4多步骤表单数据丢失求助:提交时仅获最后一步数据
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: noneto 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

