diff --git a/packages/antd/src/array-table/index.tsx b/packages/antd/src/array-table/index.tsx index 2c713c816a0..c7da826c00b 100644 --- a/packages/antd/src/array-table/index.tsx +++ b/packages/antd/src/array-table/index.tsx @@ -245,6 +245,10 @@ const ArrayTablePagination: React.FC = (props) => { ) } +const RowComp = (props: any) => { + return +} + export const ArrayTable: ComposedArrayTable = observer( (props: TableProps) => { const ref = useRef() @@ -305,14 +309,7 @@ export const ArrayTable: ComposedArrayTable = observer( {...props} /> ), - row: (props: any) => { - return ( - - ) - }, + row: RowComp, }, }} /> diff --git a/packages/core/src/__tests__/array.spec.ts b/packages/core/src/__tests__/array.spec.ts index a0f57f65655..3635d69a2cf 100644 --- a/packages/core/src/__tests__/array.spec.ts +++ b/packages/core/src/__tests__/array.spec.ts @@ -467,3 +467,50 @@ test('nest array remove', async () => { await metrics.remove(1) expect(form.fields['metrics.0.content.0.attr']).not.toBeUndefined() }) + +test('incomplete insertion of array elements', async () => { + const form = attach( + createForm({ + values: { + array: [{ aa: 1 }, { aa: 2 }, { aa: 3 }], + }, + }) + ) + const array = attach( + form.createArrayField({ + name: 'array', + }) + ) + attach( + form.createObjectField({ + name: '0', + basePath: 'array', + }) + ) + attach( + form.createField({ + name: 'aa', + basePath: 'array.0', + }) + ) + attach( + form.createObjectField({ + name: '2', + basePath: 'array', + }) + ) + attach( + form.createField({ + name: 'aa', + basePath: 'array.2', + }) + ) + expect(form.fields['array.0.aa']).not.toBeUndefined() + expect(form.fields['array.1.aa']).toBeUndefined() + expect(form.fields['array.2.aa']).not.toBeUndefined() + await array.unshift({}) + expect(form.fields['array.0.aa']).toBeUndefined() + expect(form.fields['array.1.aa']).not.toBeUndefined() + expect(form.fields['array.2.aa']).toBeUndefined() + expect(form.fields['array.3.aa']).not.toBeUndefined() +}) diff --git a/packages/core/src/shared/constants.ts b/packages/core/src/shared/constants.ts index 906d83796c0..2def15bb97d 100644 --- a/packages/core/src/shared/constants.ts +++ b/packages/core/src/shared/constants.ts @@ -24,3 +24,5 @@ export const GlobalState = { effectStart: false, effectEnd: false, } + +export const NumberIndexReg = /^\.(\d+)/ diff --git a/packages/core/src/shared/internals.ts b/packages/core/src/shared/internals.ts index a60ff9d6b0e..11c3a73447f 100644 --- a/packages/core/src/shared/internals.ts +++ b/packages/core/src/shared/internals.ts @@ -28,7 +28,7 @@ import { FieldMatchPattern, } from '../types' import { isArrayField, isGeneralField, isQuery, isVoidField } from './externals' -import { ReservedProperties, GlobalState } from './constants' +import { ReservedProperties, GlobalState, NumberIndexReg } from './constants' export const isHTMLInputEvent = (event: any, stopPropagation = true) => { if (event?.target) { @@ -104,13 +104,14 @@ export const applyFieldPatches = ( target: Record, patches: INodePatch[] ) => { - patches.forEach(({ type, address, payload }) => { + patches.forEach(({ type, address, oldAddress, payload }) => { if (type === 'remove') { - target[address].dispose() + target[address]?.dispose() delete target[address] } else if (type === 'update') { if (payload) { target[address] = payload + if (target[oldAddress] === payload) delete target[oldAddress] } if (address && payload) { buildNodeIndexes(payload, address) @@ -228,32 +229,31 @@ export const spliceArrayState = ( ...props, } const address = field.address.toString() + const addrLength = address.length const fields = field.form.fields const fieldPatches: INodePatch[] = [] const offset = insertCount - deleteCount const isArrayChildren = (identifier: string) => { - return ( - identifier.indexOf(address) === 0 && identifier.length > address.length - ) + return identifier.indexOf(address) === 0 && identifier.length > addrLength } const isAfterNode = (identifier: string) => { - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return false const index = Number(number) return index > startIndex + deleteCount - 1 } const isInsertNode = (identifier: string) => { - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return false const index = Number(number) return index >= startIndex && index < startIndex + insertCount } const isDeleteNode = (identifier: string) => { - const preStr = identifier.slice(0, address.length) - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const preStr = identifier.substring(0, addrLength) + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return false const index = Number(number) return ( @@ -265,9 +265,9 @@ export const spliceArrayState = ( } const moveIndex = (identifier: string) => { if (offset === 0) return identifier - const preStr = identifier.slice(0, address.length) - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const preStr = identifier.substring(0, addrLength) + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return identifier const index = Number(number) + offset return `${preStr}${afterStr.replace(/^\.\d+/, `.${index}`)}` @@ -281,6 +281,7 @@ export const spliceArrayState = ( fieldPatches.push({ type: 'update', address: newIdentifier, + oldAddress: identifier, payload: field, }) } @@ -305,25 +306,24 @@ export const exchangeArrayState = ( } const address = field.address.toString() const fields = field.form.fields + const addrLength = address.length const fieldPatches: INodePatch[] = [] const isArrayChildren = (identifier: string) => { - return ( - identifier.indexOf(address) === 0 && identifier.length > address.length - ) + return identifier.indexOf(address) === 0 && identifier.length > addrLength } const isFromOrToNode = (identifier: string) => { - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return false const index = Number(number) return index === toIndex || index === fromIndex } const moveIndex = (identifier: string) => { - const preStr = identifier.slice(0, address.length) - const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)[1] + const preStr = identifier.substring(0, addrLength) + const afterStr = identifier.substring(addrLength) + const number = afterStr.match(NumberIndexReg)[1] const current = Number(number) let index = current if (index === fromIndex) { @@ -343,6 +343,7 @@ export const exchangeArrayState = ( fieldPatches.push({ type: 'update', address: newIdentifier, + oldAddress: identifier, payload: field, }) if (!fields[newIdentifier]) { @@ -371,7 +372,7 @@ export const cleanupArrayChildren = (field: ArrayField, start: number) => { const isNeedCleanup = (identifier: string) => { const afterStr = identifier.slice(address.length) - const number = afterStr.match(/^\.(\d+)/)?.[1] + const number = afterStr.match(NumberIndexReg)?.[1] if (number === undefined) return false const index = Number(number) return index >= start diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f8aca4776d5..62e080f6071 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -90,6 +90,7 @@ export type HeartSubscriber = ({ export interface INodePatch { type: 'remove' | 'update' address: string + oldAddress?: string payload?: T } diff --git a/packages/react/src/components/ObjectField.tsx b/packages/react/src/components/ObjectField.tsx index 61ad59edde7..bf1f3658d68 100644 --- a/packages/react/src/components/ObjectField.tsx +++ b/packages/react/src/components/ObjectField.tsx @@ -14,7 +14,6 @@ export const ObjectField = ( const field = useAttach( form.createObjectField({ basePath: parent?.address, ...props }) ) - return ( {props.children}