Skip to content

Commit 858a2a0

Browse files
authored
fix: onValuesChange should merge with correct value (#769)
* test: test driven * chore: update values * fix: list check logic
1 parent cbf2162 commit 858a2a0

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
},
5151
"dependencies": {
5252
"@rc-component/async-validator": "^5.0.3",
53-
"@rc-component/util": "^1.3.0",
53+
"@rc-component/util": "^1.5.0",
5454
"clsx": "^2.1.1"
5555
},
5656
"devDependencies": {

src/useForm.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { merge } from '@rc-component/util/lib/utils/set';
2+
import { mergeWith } from '@rc-component/util';
23
import warning from '@rc-component/util/lib/warning';
34
import * as React from 'react';
45
import { HOOK_MARK } from './FieldContext';
@@ -783,10 +784,14 @@ export class FormStore {
783784
const { onValuesChange } = this.callbacks;
784785

785786
if (onValuesChange) {
787+
const fieldEntity = this.getFieldsMap(true).get(namePath);
786788
const changedValues = cloneByNamePathList(this.store, [namePath]);
787789
const allValues = this.getFieldsValue();
788790
// Merge changedValues into allValues to ensure allValues contains the latest changes
789-
const mergedAllValues = merge(allValues, changedValues);
791+
const mergedAllValues = mergeWith([allValues, changedValues], {
792+
// When value is array, it means trigger by Form.List which should replace directly
793+
prepareArray: current => (fieldEntity?.isList() ? [] : [...(current || [])]),
794+
});
790795
onValuesChange(changedValues, mergedAllValues);
791796
}
792797

tests/list.test.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,4 +1042,101 @@ describe('Form.List', () => {
10421042

10431043
expect(onFinishFailed).toHaveBeenCalled();
10441044
});
1045+
1046+
it('List should have correct onValuesChange', () => {
1047+
const onValuesChange = jest.fn();
1048+
1049+
const [container] = generateForm(
1050+
fields => (
1051+
<div>
1052+
{fields.map(field => (
1053+
<div key={field.key}>
1054+
<Field {...field} name={[field.name, 'first']}>
1055+
<Input />
1056+
</Field>
1057+
<Field {...field} name={[field.name, 'last']}>
1058+
<Input />
1059+
</Field>
1060+
</div>
1061+
))}
1062+
</div>
1063+
),
1064+
{
1065+
initialValues: {
1066+
list: [{ first: 'light' }],
1067+
},
1068+
onValuesChange,
1069+
},
1070+
);
1071+
1072+
fireEvent.change(getInput(container, 1), { target: { value: 'little' } });
1073+
expect(onValuesChange).toHaveBeenCalledWith(
1074+
{ list: [{ last: 'little' }] },
1075+
{ list: [{ first: 'light', last: 'little' }] },
1076+
);
1077+
});
1078+
1079+
it('should correctly merge array-valued fields within Form.List items without losing data', async () => {
1080+
const TagInput = ({ value = [], onChange }: any) => (
1081+
<input
1082+
data-testid="tag-input"
1083+
value={value.join(',')}
1084+
onChange={e => {
1085+
const newValue = e.target.value
1086+
.split(',')
1087+
.map(s => s.trim())
1088+
.filter(Boolean);
1089+
onChange(newValue);
1090+
}}
1091+
/>
1092+
);
1093+
1094+
const onValuesChange = jest.fn();
1095+
1096+
const [container] = generateForm(
1097+
fields => (
1098+
<>
1099+
{fields.map(field => (
1100+
<div key={field.key}>
1101+
<Field {...field} name={[field.name, 'name']}>
1102+
<Input />
1103+
</Field>
1104+
<Field {...field} name={[field.name, 'tags']}>
1105+
<TagInput />
1106+
</Field>
1107+
</div>
1108+
))}
1109+
</>
1110+
),
1111+
{
1112+
initialValues: {
1113+
list: [{ name: 'John', tags: ['react', 'js'] }],
1114+
},
1115+
onValuesChange,
1116+
},
1117+
);
1118+
1119+
const tagInput = container.querySelector('input[data-testid="tag-input"]') as HTMLElement;
1120+
1121+
await act(async () => {
1122+
fireEvent.change(tagInput, {
1123+
target: { value: 'react,ts' },
1124+
});
1125+
});
1126+
1127+
expect(onValuesChange).toHaveBeenCalledWith(
1128+
{ list: [{ tags: ['react', 'ts'] }] }, // changedValues
1129+
{ list: [{ name: 'John', tags: ['react', 'ts'] }] }, // allValues
1130+
);
1131+
onValuesChange.mockReset();
1132+
1133+
await act(async () => {
1134+
fireEvent.change(tagInput, { target: { value: 'react,ts,redux' } });
1135+
});
1136+
1137+
expect(onValuesChange).toHaveBeenLastCalledWith(
1138+
{ list: [{ tags: ['react', 'ts', 'redux'] }] },
1139+
{ list: [{ name: 'John', tags: ['react', 'ts', 'redux'] }] },
1140+
);
1141+
});
10451142
});

0 commit comments

Comments
 (0)