Skip to content

Commit da85b39

Browse files
RylanBotuyarntdesign-bot
authored
fix(FormList): failed to initialize values (#4005)
* fix(FormList): failed to initialize values * fix: add type check * fix: incorrect value in `add` * fix: clone * fix: `shouldUpdate` with `FormList` * fix: ensure `add()` works * fix: ensure `fullPath` correct * chore: simplify path logic * fix: `store` is reset to its initial state even when `remove` is called * chore: revert conflict change * chore: stash changelog [ci skip] --------- Co-authored-by: wū yāng <[email protected]> Co-authored-by: tdesign-bot <[email protected]>
1 parent 93efd5e commit da85b39

File tree

9 files changed

+558
-176
lines changed

9 files changed

+558
-176
lines changed

packages/components/form/FormItem.tsx

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
CloseCircleFilledIcon as TdCloseCircleFilledIcon,
55
ErrorCircleFilledIcon as TdErrorCircleFilledIcon,
66
} from 'tdesign-icons-react';
7-
import { get, isEqual, isFunction, isObject, isString, set, unset } from 'lodash-es';
7+
import { get, isEqual, isFunction, isObject, isString, set } from 'lodash-es';
88

99
import useConfig from '../hooks/useConfig';
1010
import useDefaultProps from '../hooks/useDefaultProps';
@@ -81,7 +81,14 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
8181
onFormItemValueChange,
8282
} = useFormContext();
8383

84-
const { name: formListName, rules: formListRules, formListMapRef, form: formOfFormList } = useFormListContext();
84+
const {
85+
name: formListName,
86+
fullPath: parentFullPath,
87+
rules: formListRules,
88+
formListMapRef,
89+
form: formOfFormList,
90+
} = useFormListContext();
91+
8592
const props = useDefaultProps<FormItemProps>(originalProps, formItemDefaultProps);
8693

8794
const {
@@ -104,10 +111,15 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
104111
requiredMark = requiredMarkFromContext,
105112
} = props;
106113

107-
const { fullPath: parentFullPath } = useFormListContext();
108-
const fullPath = concatName(parentFullPath, name);
114+
/* 用于处理嵌套 Form 的情况 (例如 FormList 内有一个 Dialog + Form) */
115+
const isSameForm = useMemo(() => isEqual(form, formOfFormList), [form, formOfFormList]);
116+
117+
const fullPath = useMemo(() => {
118+
const validParentFullPath = formListName && isSameForm ? parentFullPath : undefined;
119+
return concatName(validParentFullPath, name);
120+
}, [formListName, parentFullPath, name, isSameForm]);
109121

110-
const { getDefaultInitialData } = useFormItemInitialData(name, fullPath);
122+
const { defaultInitialData } = useFormItemInitialData(name, fullPath, initialData, children);
111123

112124
const [, forceUpdate] = useState({}); // custom render state
113125
const [freeShowErrorMessage, setFreeShowErrorMessage] = useState(undefined);
@@ -116,12 +128,7 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
116128
const [verifyStatus, setVerifyStatus] = useState('validating');
117129
const [resetValidating, setResetValidating] = useState(false);
118130
const [needResetField, setNeedResetField] = useState(false);
119-
const [formValue, setFormValue] = useState(() =>
120-
getDefaultInitialData({
121-
children,
122-
initialData,
123-
}),
124-
);
131+
const [formValue, setFormValue] = useState(defaultInitialData);
125132

126133
const formItemRef = useRef<FormItemInstance>(null); // 当前 formItem 实例
127134
const innerFormItemsRef = useRef([]);
@@ -130,7 +137,6 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
130137
const valueRef = useRef(formValue); // 当前最新值
131138
const errorListMapRef = useRef(new Map());
132139

133-
const isSameForm = useMemo(() => isEqual(form, formOfFormList), [form, formOfFormList]); // 用于处理 Form 嵌套的情况
134140
const snakeName = []
135141
.concat(isSameForm ? formListName : undefined, name)
136142
.filter((item) => item !== undefined)
@@ -325,10 +331,7 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
325331

326332
function getResetValue(resetType: TdFormProps['resetType']): ValueType {
327333
if (resetType === 'initial') {
328-
return getDefaultInitialData({
329-
children,
330-
initialData,
331-
});
334+
return defaultInitialData;
332335
}
333336

334337
let emptyValue: ValueType;
@@ -413,26 +416,21 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
413416
}, [shouldUpdate, form]);
414417

415418
useEffect(() => {
416-
// 记录填写 name 属性 formItem
417419
if (typeof name === 'undefined') return;
418420

419-
// FormList 下特殊处理
420-
if (formListName && isSameForm) {
421-
formListMapRef.current.set(fullPath, formItemRef);
422-
set(form?.store, fullPath, formValue);
423-
return () => {
424-
// eslint-disable-next-line react-hooks/exhaustive-deps
425-
formListMapRef.current.delete(fullPath);
426-
unset(form?.store, fullPath);
427-
};
428-
}
429-
if (!formMapRef) return;
430-
formMapRef.current.set(fullPath, formItemRef);
431-
set(form?.store, fullPath, formValue);
421+
const isFormList = formListName && isSameForm;
422+
const mapRef = isFormList ? formListMapRef : formMapRef;
423+
if (!mapRef.current) return;
424+
425+
// 注册实例
426+
mapRef.current.set(fullPath, formItemRef);
427+
428+
// 初始化
429+
set(form?.store, fullPath, defaultInitialData);
430+
setFormValue(defaultInitialData);
431+
432432
return () => {
433-
// eslint-disable-next-line react-hooks/exhaustive-deps
434-
formMapRef.current.delete(fullPath);
435-
unset(form?.store, fullPath);
433+
mapRef.current.delete(fullPath);
436434
};
437435
// eslint-disable-next-line react-hooks/exhaustive-deps
438436
}, [snakeName, formListName]);

packages/components/form/FormList.tsx

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
2-
import { castArray, get, isEqual, merge, set, unset } from 'lodash-es';
2+
import { castArray, cloneDeep, get, isEqual, merge, set, unset } from 'lodash-es';
3+
34
import log from '@tdesign/common-js/log/index';
45
import { FormListContext, useFormContext, useFormListContext } from './FormContext';
56
import { HOOK_MARK } from './hooks/useForm';
@@ -34,10 +35,11 @@ const FormList: React.FC<TdFormListProps> = (props) => {
3435
} else {
3536
propsInitialData = get(initialDataFromForm, fullPath);
3637
}
37-
return propsInitialData;
38-
}, [fullPath, parentFullPath, initialDataFromForm, parentInitialData, props.initialData]);
38+
return cloneDeep(propsInitialData || []);
39+
}, [props.initialData, fullPath, parentFullPath, parentInitialData, initialDataFromForm]);
40+
41+
const [formListValue, setFormListValue] = useState(() => get(form?.store, fullPath) || initialData);
3942

40-
const [formListValue, setFormListValue] = useState(() => get(form?.store, fullPath) || initialData || []);
4143
const [fields, setFields] = useState<FormListField[]>(() =>
4244
formListValue.map((data, index) => ({
4345
data: { ...data },
@@ -57,20 +59,6 @@ const FormList: React.FC<TdFormListProps> = (props) => {
5759
.filter((item) => item !== undefined)
5860
.toString(); // 转化 name
5961

60-
const buildDefaultFieldMap = () => {
61-
if (formListMapRef.current.size <= 0) return {};
62-
const defaultValues: Record<string, any> = {};
63-
formListMapRef.current.forEach((item, itemPath) => {
64-
const itemPathArray = convertNameToArray(itemPath);
65-
const isChildField = itemPathArray.length === convertNameToArray(fullPath).length + 2;
66-
if (!isChildField) return;
67-
const fieldName = itemPathArray[itemPathArray.length - 1];
68-
// add 没有传参时,构建一个包含所有子字段的对象用于占位,确保回调给用户的数据结构完整
69-
defaultValues[fieldName] = item.current.initialData;
70-
});
71-
return defaultValues;
72-
};
73-
7462
const updateFormList = (newFields: any, newFormListValue: any) => {
7563
setFields(newFields);
7664
setFormListValue(newFormListValue);
@@ -89,11 +77,7 @@ const FormList: React.FC<TdFormListProps> = (props) => {
8977
isListField: true,
9078
});
9179
const newFormListValue = [...formListValue];
92-
if (defaultValue !== undefined) {
93-
newFormListValue.splice(index, 0, defaultValue);
94-
} else {
95-
newFormListValue.splice(index, 0, buildDefaultFieldMap());
96-
}
80+
newFormListValue.splice(index, 0, cloneDeep(defaultValue));
9781
updateFormList(newFields, newFormListValue);
9882
},
9983
remove(index: number | number[]) {
@@ -141,7 +125,9 @@ const FormList: React.FC<TdFormListProps> = (props) => {
141125

142126
useEffect(() => {
143127
if (!name || !formMapRef) return;
128+
// 初始化
144129
formMapRef.current.set(fullPath, formListRef);
130+
set(form?.store, fullPath, formListValue);
145131
return () => {
146132
// eslint-disable-next-line react-hooks/exhaustive-deps
147133
formMapRef.current.delete(fullPath);
@@ -173,10 +159,14 @@ const FormList: React.FC<TdFormListProps> = (props) => {
173159
return new Promise((resolve) => {
174160
Promise.all(validates).then((validateResult) => {
175161
validateResult.forEach((result) => {
162+
if (typeof result !== 'object') return;
176163
const errorValue = Object.values(result)[0];
177164
merge(resultList, errorValue);
178165
});
179-
const errorItems = validateResult.filter((item) => Object.values(item)[0] !== true);
166+
const errorItems = validateResult.filter((item) => {
167+
if (typeof item !== 'object') return;
168+
return Object.values(item)[0] !== true;
169+
});
180170
if (errorItems.length) {
181171
resolve({ [snakeName]: resultList });
182172
} else {
@@ -203,17 +193,16 @@ const FormList: React.FC<TdFormListProps> = (props) => {
203193
const resetType = type || resetTypeFromContext;
204194
if (resetType === 'initial') {
205195
const currentData = get(form?.store, fullPath);
206-
const data = initialData || [];
207196
if (isEqual(currentData, initialData)) return;
208-
setFormListValue(data);
209-
const newFields = data?.map((data, index) => ({
197+
setFormListValue(initialData);
198+
const newFields = initialData?.map((data, index) => ({
210199
data: { ...data },
211200
key: (globalKey += 1),
212201
name: index,
213202
isListField: true,
214203
}));
215204
setFields(newFields);
216-
set(form?.store, fullPath, data);
205+
set(form?.store, fullPath, initialData);
217206
} else {
218207
// 重置为空
219208
setFormListValue([]);

0 commit comments

Comments
 (0)