diff --git a/docs/demo/value-form-event.md b/docs/demo/value-form-event.md
new file mode 100644
index 000000000..d8b00535a
--- /dev/null
+++ b/docs/demo/value-form-event.md
@@ -0,0 +1,4 @@
+## ValueFromEvent
+
+
+
diff --git a/docs/examples/value-form-event.tsx b/docs/examples/value-form-event.tsx
new file mode 100644
index 000000000..5cde5167a
--- /dev/null
+++ b/docs/examples/value-form-event.tsx
@@ -0,0 +1,197 @@
+import Form, { Field } from 'rc-field-form';
+import React from 'react';
+import Input from './components/Input';
+
+const Divider = (props: React.PropsWithChildren) => (
+
+ 🔽🔽🔽
+ {props.children}
+ 🔽🔽🔽
+
+)
+
+interface CustomValue {
+ timestamp: number,
+ date: Date,
+ formatted: string,
+}
+
+type FormData = {
+ // Input
+ text?: string; // default
+
+ // ========== 🔽 event.target.checked
+ checkbox?: boolean;
+ radio?: boolean;
+ // ========== 🔽 event.target.valueAsNumber
+ number?: number;
+ range?: number;
+ // ========== 🔽 event.target.files
+ file?: FileList;
+
+ // ========== 🔽 event.target.value(default)!!!
+ password?: string;
+ search?: string;
+ email?: string;
+ url?: string;
+ tel?: string;
+ date?: number;
+ time?: string;
+ dateTimeLocal?: string;
+ week?: string;
+ month?: string;
+ color?: string;
+
+ // Select
+ select?: string;
+ // Textarea
+ textarea?: string;
+
+ // Custom
+ custom?: CustomValue;
+};
+
+
+function App() {
+ const [form] = Form.useForm();
+
+ const initialValues: FormData = {
+ url: 'https://github.com/react-component/field-form',
+ email: 'wxh16144@users.noreply.github.com',
+ }
+
+ return (
+
+ );
+}
+
+export default App
diff --git a/src/utils/valueUtil.ts b/src/utils/valueUtil.ts
index eb64966aa..33f4b3680 100644
--- a/src/utils/valueUtil.ts
+++ b/src/utils/valueUtil.ts
@@ -94,8 +94,57 @@ export function isSimilar(source: SimilarObject, target: SimilarObject) {
export function defaultGetValueFromEvent(valuePropName: string, ...args: EventArgs) {
const event = args[0];
- if (event && event.target && typeof event.target === 'object' && valuePropName in event.target) {
- return (event.target as HTMLInputElement)[valuePropName];
+
+ /**
+ * `target` is the element that triggered the event (e.g., the user clicked on)
+ * `currentTarget` is the element that the event listener is attached to.
+ */
+ const nodeName = (event?.target?.nodeName ?? '').toLowerCase();
+
+ if (nodeName === 'input') {
+ const type = (event.target.type ?? 'text').toLowerCase();
+
+ if (['checkbox', 'radio'].includes(type)) {
+ return event.target.checked;
+ }
+
+ // `datetime` Obsolete
+ if (['number', 'range'].includes(type)) {
+ // https://caniuse.com/?search=valueAsNumber, support IE11+
+ return event.target.valueAsNumber ?? event.target.value;
+ }
+
+ /**
+ * Problems with backfilling the data collected, so it is not processed here
+ * @see https://devlog.willcodefor.beer/pages/use-valueasnumber-and-valueasdate-on-inputs/
+ * `datetime` Obsolete
+ */
+ // if (['date', 'datetime-local'].includes(type)) {
+ // const _value = {
+ // timestamp: event.target.valueAsNumber,
+ // date: event.target.valueAsDate,
+ // formatted: event.target.value,
+ // }
+ // }
+
+ if (type === 'file') {
+ return event.target.files;
+ }
+
+ /**
+ * text password search email url week month tel color time
+ * [submit, reset, button, image, hidden] ?? i dont care :)
+ */
+ // return event.target.value; // valuePropName default is 'value'
+ }
+
+ // because `valuePropName` default is `value`
+ // if (['textarea', 'select'].includes(nodeName)) {
+ // return event.target.value;
+ // }
+
+ if (typeof event?.target === 'object' && valuePropName in event.target) {
+ return event.target[valuePropName];
}
return event;
diff --git a/tests/legacy/dynamic-binding.test.tsx b/tests/legacy/dynamic-binding.test.tsx
index 7aead6a14..8c7a18a22 100644
--- a/tests/legacy/dynamic-binding.test.tsx
+++ b/tests/legacy/dynamic-binding.test.tsx
@@ -44,10 +44,11 @@ describe('legacy.dynamic-binding', () => {
rerender();
- expect(getInput(container, '#text')?.value).toBe('456');
- expect(form.current?.getFieldValue('name')).toBe('456');
+ expect(getInput(container, '#text')?.value).toBe("456");
+ expect(form.current?.getFieldValue('name')).toBe(456);
const values = await form.current?.validateFields();
- expect(values.name).toBe('456');
+ expect(values.name).toBe(456);
+ expect(typeof values.name).toBe('number');
});
// [Legacy] We do not remove value in Field Form
@@ -141,9 +142,10 @@ describe('legacy.dynamic-binding', () => {
rerender();
expect(getInput(container, '#text')?.value).toBe('456');
- expect(form.current?.getFieldValue(['name', 'xxx'])).toBe('456');
+ expect(form.current?.getFieldValue(['name', 'xxx'])).toBe(456);
const values = await form.current?.validateFields();
- expect(values.name.xxx).toBe('456');
+ expect(values.name.xxx).toBe(456);
+ expect(typeof values.name.xxx).toBe('number');
});
it('input with different keys', async () => {
@@ -178,10 +180,11 @@ describe('legacy.dynamic-binding', () => {
rerender();
expect(getInput(container, '#text')?.value).toBe('456');
- expect(form.current?.getFieldValue('name')).toBe('456');
+ expect(form.current?.getFieldValue('name')).toBe(456);
const values = await form.current?.validateFields();
- expect(values.name).toBe('456');
+ expect(values.name).toBe(456);
+ expect(typeof values.name).toBe('number');
});
it('submit without removed fields', async () => {