diff --git a/.changeset/fix-form-useform-hook.md b/.changeset/fix-form-useform-hook.md
new file mode 100644
index 00000000..d50c733e
--- /dev/null
+++ b/.changeset/fix-form-useform-hook.md
@@ -0,0 +1,5 @@
+---
+"@tiny-design/react": patch
+---
+
+fix: preserve FormInstance across re-renders in useForm hook
diff --git a/packages/react/src/form/demo/StepForm.tsx b/packages/react/src/form/demo/StepForm.tsx
new file mode 100644
index 00000000..f5524908
--- /dev/null
+++ b/packages/react/src/form/demo/StepForm.tsx
@@ -0,0 +1,166 @@
+import React, { useState } from 'react';
+import { Form, Input, InputNumber, Steps, Button, Flex, Result } from '@tiny-design/react';
+
+const stepFields: string[][] = [
+ ['username', 'email'],
+ ['fullName', 'age', 'phone'],
+];
+
+export default function StepFormDemo() {
+ const [form] = Form.useForm({
+ username: '',
+ email: '',
+ fullName: '',
+ age: '',
+ phone: '',
+ });
+ const [current, setCurrent] = useState(0);
+
+ const validateStep = (step: number): boolean => {
+ const fields = stepFields[step];
+ if (!fields) return true;
+ fields.forEach((name) => form.validateField(name));
+ return fields.every((name) => !form.getFieldError(name));
+ };
+
+ const handleNext = () => {
+ if (validateStep(current)) {
+ setCurrent(current + 1);
+ }
+ };
+
+ const handlePrev = () => {
+ setCurrent(current - 1);
+ };
+
+ const handleFinish = () => {
+ setCurrent(3);
+ };
+
+ const handleReset = () => {
+ form.resetFields();
+ setCurrent(0);
+ };
+
+ const stepStyle = (step: number): React.CSSProperties =>
+ current !== step ? { display: 'none' } : {};
+
+ return (
+
+
+
+
+
+
+
+
+ {current < 3 ? (
+
+
+ {current > 0 && (
+
+ )}
+ {current < 2 && (
+
+ )}
+ {current === 2 && (
+
+ )}
+
+
+
+ ) : (
+
+ Register Another
+
+ }
+ />
+ )}
+
+ );
+}
diff --git a/packages/react/src/form/index.md b/packages/react/src/form/index.md
index 91c66956..b8510238 100644
--- a/packages/react/src/form/index.md
+++ b/packages/react/src/form/index.md
@@ -16,6 +16,8 @@ import AsyncSubmitDemo from './demo/AsyncSubmit';
import AsyncSubmitSource from './demo/AsyncSubmit.tsx?raw';
import OtherControlsDemo from './demo/OtherControls';
import OtherControlsSource from './demo/OtherControls.tsx?raw';
+import StepFormDemo from './demo/StepForm';
+import StepFormSource from './demo/StepForm.tsx?raw';
# Form
@@ -124,6 +126,15 @@ A versatile example.
+
+### Multi-Step Form
+
+A multi-step registration form. Each step validates its fields via the form instance before proceeding to the next step.
+
+
+
+
+
## API
### Form
diff --git a/packages/react/src/form/index.zh_CN.md b/packages/react/src/form/index.zh_CN.md
index 1e79e38d..1e3ba4ed 100644
--- a/packages/react/src/form/index.zh_CN.md
+++ b/packages/react/src/form/index.zh_CN.md
@@ -16,6 +16,8 @@ import AsyncSubmitDemo from './demo/AsyncSubmit';
import AsyncSubmitSource from './demo/AsyncSubmit.tsx?raw';
import OtherControlsDemo from './demo/OtherControls';
import OtherControlsSource from './demo/OtherControls.tsx?raw';
+import StepFormDemo from './demo/StepForm';
+import StepFormSource from './demo/StepForm.tsx?raw';
# Form
@@ -121,6 +123,15 @@ const { Item, useForm, FormInstance } = Form;
+
+### 分步表单
+
+多步骤注册表单。每一步通过表单实例校验当前步骤的字段后才能进入下一步。
+
+
+
+
+
## API
### Form
diff --git a/packages/react/src/form/use-form.ts b/packages/react/src/form/use-form.ts
index 564ff0f3..0d7e9faf 100644
--- a/packages/react/src/form/use-form.ts
+++ b/packages/react/src/form/use-form.ts
@@ -1,5 +1,10 @@
+import { useRef } from 'react';
import FormInstance, { FormValues } from './form-instance';
export default function useForm(initialValues: FormValues = {}): [FormInstance] {
- return [new FormInstance(initialValues)];
+ const ref = useRef(null);
+ if (!ref.current) {
+ ref.current = new FormInstance(initialValues);
+ }
+ return [ref.current];
}