Skip to content
65 changes: 32 additions & 33 deletions packages/components/form/FormItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { get, isEqual, isFunction, isObject, isString, set, unset } from 'lodash-es';
import React, { forwardRef, ReactNode, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import {
CheckCircleFilledIcon as TdCheckCircleFilledIcon,
CloseCircleFilledIcon as TdCloseCircleFilledIcon,
ErrorCircleFilledIcon as TdErrorCircleFilledIcon,
} from 'tdesign-icons-react';
import { get, isEqual, isFunction, isObject, isString, set } from 'lodash-es';

import useConfig from '../hooks/useConfig';
import useDefaultProps from '../hooks/useDefaultProps';
Expand Down Expand Up @@ -81,7 +81,14 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
onFormItemValueChange,
} = useFormContext();

const { name: formListName, rules: formListRules, formListMapRef, form: formOfFormList } = useFormListContext();
const {
name: formListName,
fullPath: parentFullPath,
rules: formListRules,
formListMapRef,
form: formOfFormList,
} = useFormListContext();

const props = useDefaultProps<FormItemProps>(originalProps, formItemDefaultProps);

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

const { fullPath: parentFullPath } = useFormListContext();
const fullPath = concatName(parentFullPath, name);
/* 用于处理嵌套 Form 的情况 (例如 FormList 内有一个 Dialog + Form) */
const isSameForm = useMemo(() => isEqual(form, formOfFormList), [form, formOfFormList]);

const fullPath = useMemo(() => {
const validParentFullPath = formListName && isSameForm ? parentFullPath : undefined;
return concatName(validParentFullPath, name);
}, [formListName, parentFullPath, name, isSameForm]);

const { getDefaultInitialData } = useFormItemInitialData(name, fullPath);
const { defaultInitialData } = useFormItemInitialData(name, fullPath, initialData, children);

const [, forceUpdate] = useState({}); // custom render state
const [freeShowErrorMessage, setFreeShowErrorMessage] = useState(undefined);
Expand All @@ -116,12 +128,7 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
const [verifyStatus, setVerifyStatus] = useState('validating');
const [resetValidating, setResetValidating] = useState(false);
const [needResetField, setNeedResetField] = useState(false);
const [formValue, setFormValue] = useState(() =>
getDefaultInitialData({
children,
initialData,
}),
);
const [formValue, setFormValue] = useState(defaultInitialData);

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

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

function getResetValue(resetType: TdFormProps['resetType']): ValueType {
if (resetType === 'initial') {
return getDefaultInitialData({
children,
initialData,
});
return defaultInitialData;
}

let emptyValue: ValueType;
Expand Down Expand Up @@ -413,26 +416,22 @@ const FormItem = forwardRef<FormItemInstance, FormItemProps>((originalProps, ref
}, [shouldUpdate, form]);

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

// FormList 下特殊处理
if (formListName && isSameForm) {
formListMapRef.current.set(fullPath, formItemRef);
set(form?.store, fullPath, formValue);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
formListMapRef.current.delete(fullPath);
unset(form?.store, fullPath);
};
}
if (!formMapRef) return;
formMapRef.current.set(fullPath, formItemRef);
set(form?.store, fullPath, formValue);
const isFormList = formListName && isSameForm;
const mapRef = isFormList ? formListMapRef : formMapRef;
if (!mapRef.current) return;

// 注册实例
mapRef.current.set(fullPath, formItemRef);

// 初始化
set(form?.store, fullPath, defaultInitialData);
setFormValue(defaultInitialData);

return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
formMapRef.current.delete(fullPath);
unset(form?.store, fullPath);
mapRef.current.delete(fullPath);
set(form?.store, fullPath, defaultInitialData);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [snakeName, formListName]);
Expand Down
45 changes: 17 additions & 28 deletions packages/components/form/FormList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { castArray, get, isEqual, merge, set, unset } from 'lodash-es';
import { castArray, cloneDeep, get, isEqual, merge, set, unset } from 'lodash-es';

import log from '@tdesign/common-js/log/index';
import { FormListContext, useFormContext, useFormListContext } from './FormContext';
import { HOOK_MARK } from './hooks/useForm';
Expand Down Expand Up @@ -34,10 +35,11 @@ const FormList: React.FC<TdFormListProps> = (props) => {
} else {
propsInitialData = get(initialDataFromForm, fullPath);
}
return propsInitialData;
}, [fullPath, parentFullPath, initialDataFromForm, parentInitialData, props.initialData]);
return cloneDeep(propsInitialData || []);
}, [props.initialData, fullPath, parentFullPath, parentInitialData, initialDataFromForm]);

const [formListValue, setFormListValue] = useState(() => get(form?.store, fullPath) || initialData);

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

const buildDefaultFieldMap = () => {
if (formListMapRef.current.size <= 0) return {};
const defaultValues: Record<string, any> = {};
formListMapRef.current.forEach((item, itemPath) => {
const itemPathArray = convertNameToArray(itemPath);
const isChildField = itemPathArray.length === convertNameToArray(fullPath).length + 2;
if (!isChildField) return;
const fieldName = itemPathArray[itemPathArray.length - 1];
// add 没有传参时,构建一个包含所有子字段的对象用于占位,确保回调给用户的数据结构完整
defaultValues[fieldName] = item.current.initialData;
});
return defaultValues;
};

const updateFormList = (newFields: any, newFormListValue: any) => {
setFields(newFields);
setFormListValue(newFormListValue);
Expand All @@ -89,11 +77,7 @@ const FormList: React.FC<TdFormListProps> = (props) => {
isListField: true,
});
const newFormListValue = [...formListValue];
if (defaultValue !== undefined) {
newFormListValue.splice(index, 0, defaultValue);
} else {
newFormListValue.splice(index, 0, buildDefaultFieldMap());
}
newFormListValue.splice(index, 0, cloneDeep(defaultValue));
updateFormList(newFields, newFormListValue);
},
remove(index: number | number[]) {
Expand Down Expand Up @@ -141,7 +125,9 @@ const FormList: React.FC<TdFormListProps> = (props) => {

useEffect(() => {
if (!name || !formMapRef) return;
// 初始化
formMapRef.current.set(fullPath, formListRef);
set(form?.store, fullPath, formListValue);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
formMapRef.current.delete(fullPath);
Expand Down Expand Up @@ -173,10 +159,14 @@ const FormList: React.FC<TdFormListProps> = (props) => {
return new Promise((resolve) => {
Promise.all(validates).then((validateResult) => {
validateResult.forEach((result) => {
if (typeof result !== 'object') return;
const errorValue = Object.values(result)[0];
merge(resultList, errorValue);
});
const errorItems = validateResult.filter((item) => Object.values(item)[0] !== true);
const errorItems = validateResult.filter((item) => {
if (typeof item !== 'object') return;
return Object.values(item)[0] !== true;
});
if (errorItems.length) {
resolve({ [snakeName]: resultList });
} else {
Expand All @@ -203,17 +193,16 @@ const FormList: React.FC<TdFormListProps> = (props) => {
const resetType = type || resetTypeFromContext;
if (resetType === 'initial') {
const currentData = get(form?.store, fullPath);
const data = initialData || [];
if (isEqual(currentData, initialData)) return;
setFormListValue(data);
const newFields = data?.map((data, index) => ({
setFormListValue(initialData);
const newFields = initialData?.map((data, index) => ({
data: { ...data },
key: (globalKey += 1),
name: index,
isListField: true,
}));
setFields(newFields);
set(form?.store, fullPath, data);
set(form?.store, fullPath, initialData);
} else {
// 重置为空
setFormListValue([]);
Expand Down
Loading
Loading