Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/demo/getFieldsName.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## getFieldsName

<code src="../examples/getFieldsName.tsx"></code>
25 changes: 25 additions & 0 deletions docs/examples/getFieldsName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Form, { Field } from 'rc-field-form';
import React from 'react';
import Input from './components/Input';

export default () => {
const [form] = Form.useForm();

return (
<Form form={form}>
<Field name="username">
<Input placeholder="Username" />
</Field>
<Field name={['profile', 'email']}>
<Input placeholder="profile.email" />
</Field>
<Field shouldUpdate>
{() => (
<pre style={{ marginTop: 16, fontSize: 12 }}>
{JSON.stringify(form.getFieldsName(), null, 2)}
</pre>
)}
</Field>
</Form>
);
};
1 change: 1 addition & 0 deletions src/FieldContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const Context = React.createContext<InternalFormInstance>({
getFieldsValue: warningFunc,
getFieldError: warningFunc,
getFieldWarning: warningFunc,
getFieldsName: warningFunc,
getFieldsError: warningFunc,
isFieldsTouched: warningFunc,
isFieldTouched: warningFunc,
Expand Down
6 changes: 6 additions & 0 deletions src/hooks/useForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class FormStore {
getFieldsValue: this.getFieldsValue,
getFieldError: this.getFieldError,
getFieldWarning: this.getFieldWarning,
getFieldsName: this.getFieldsName,
getFieldsError: this.getFieldsError,
isFieldsTouched: this.isFieldsTouched,
isFieldTouched: this.isFieldTouched,
Expand Down Expand Up @@ -345,6 +346,11 @@ export class FormStore {
return mergedValues;
};

private getFieldsName = (): InternalNamePath[] => {
this.warningUnhooked();
return this.getFieldEntities(true).map(entity => entity.getNamePath());
};
Comment on lines +349 to +352
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of getFieldsName returns all registered field entities, which may include duplicate name paths if multiple Field components share the same name. While this is consistent with getFieldsError, it might be more useful for consumers of getFieldsName to receive unique name paths, especially if they intend to use these paths with getFieldsValue. Consider if deduplication is desired here.


private getFieldValue = (name: NamePath) => {
this.warningUnhooked();

Expand Down
1 change: 1 addition & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export interface FormInstance<Values = any> {
getFieldError: (name: NamePath<Values>) => string[];
getFieldsError: (nameList?: NamePath<Values>[]) => FieldError[];
getFieldWarning: (name: NamePath<Values>) => string[];
getFieldsName: () => NamePath<Values>[];
isFieldsTouched: ((nameList?: NamePath<Values>[], allFieldsTouched?: boolean) => boolean) &
((allFieldsTouched?: boolean) => boolean);
isFieldTouched: (name: NamePath<Values>) => boolean;
Expand Down
98 changes: 98 additions & 0 deletions tests/getFieldsName.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { render } from '@testing-library/react';
import React from 'react';
import Form, { Field, List } from '../src';
import type { FormInstance } from '../src';
import { Input } from './common/InfoField';

describe('getFieldsName', () => {
it('returns empty array when no named fields', () => {
const formRef = React.createRef<FormInstance>();
render(<Form ref={formRef} />);
expect(formRef.current!.getFieldsName()).toEqual([]);
});

it('returns name paths of registered fields', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef}>
<Field name="username">
<Input />
</Field>
<Field name={['profile', 'email']}>
<Input />
</Field>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([['username'], ['profile', 'email']]);
});

it('excludes field without name', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef}>
<Field name="a">
<Input />
</Field>
<Field>{() => null}</Field>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([['a']]);
});

it('includes one entry per Field with the same name', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef}>
<Field name="x">
<Input />
</Field>
<Field name="x">
<Input />
</Field>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([['x'], ['x']]);
});
Comment on lines +42 to +55
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

测试描述存在歧义,建议澄清

'includes one entry per Field with the same name' 可被误读为"同名字段只返回一条"(即去重),而实际行为恰好相反——不做去重,每个注册的 Field 实例均贡献一条记录。建议将描述改为明确表达非去重语义,例如:

✏️ 建议的描述改法
-  it('includes one entry per Field with the same name', () => {
+  it('does not deduplicate Fields sharing the same name', () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('includes one entry per Field with the same name', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef}>
<Field name="x">
<Input />
</Field>
<Field name="x">
<Input />
</Field>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([['x'], ['x']]);
});
it('does not deduplicate Fields sharing the same name', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef}>
<Field name="x">
<Input />
</Field>
<Field name="x">
<Input />
</Field>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([['x'], ['x']]);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/getFieldsName.test.tsx` around lines 42 - 55, Update the ambiguous test
title in the spec that currently reads "includes one entry per Field with the
same name" to explicitly state the non-deduplication behavior so readers won't
misinterpret it as "deduplicates by name"; locate the test case using the
it(...) block that references formRef.current!.getFieldsName() in
tests/getFieldsName.test.tsx and replace the description with something clearer
like "returns one entry per Field instance even when names are identical" or
"does not deduplicate fields with the same name" to accurately reflect the
behavior.


it('updates when field unmounts', () => {
const formRef = React.createRef<FormInstance>();
const Demo = ({ show }: { show: boolean }) => (
<Form ref={formRef}>
<Field name="keep">
<Input />
</Field>
{show ? (
<Field name="toggle">
<Input />
</Field>
) : null}
</Form>
);
const { rerender } = render(<Demo show />);
expect(formRef.current!.getFieldsName()).toEqual([['keep'], ['toggle']]);
rerender(<Demo show={false} />);
expect(formRef.current!.getFieldsName()).toEqual([['keep']]);
});

it('includes Form.List item fields', () => {
const formRef = React.createRef<FormInstance>();
render(
<Form ref={formRef} initialValues={{ list: ['a', 'b'] }}>
<List name="list">
{fields =>
fields.map(f => (
<Field {...f} key={f.key}>
<Input />
</Field>
))
}
</List>
</Form>,
);
expect(formRef.current!.getFieldsName()).toEqual([
['list', 0],
['list', 1],
['list'],
]);
});
});