Skip to content
Merged
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
64 changes: 31 additions & 33 deletions packages/components/form/FormItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
CloseCircleFilledIcon as TdCloseCircleFilledIcon,
ErrorCircleFilledIcon as TdErrorCircleFilledIcon,
} from 'tdesign-icons-react';
import { get, isEqual, isFunction, isObject, isString, set, unset } from 'lodash-es';
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,21 @@ 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);
};
// 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