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 ? ( +
+
+ + + + + + +
+ +
+ + + + + + + + + +
+ +
+ + {form.getFieldValue('username')} + + + {form.getFieldValue('email')} + + + {form.getFieldValue('fullName')} + + + {form.getFieldValue('age')} + + + {form.getFieldValue('phone')} + +
+ + + + {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]; }