1
1
import * as React from 'react' ;
2
2
import warning from 'warning' ;
3
- import { InternalNamePath , NamePath , InternalFormInstance , StoreValue } from './interface' ;
4
- import FieldContext , { HOOK_MARK } from './FieldContext' ;
3
+ import { InternalNamePath , NamePath , StoreValue } from './interface' ;
4
+ import FieldContext from './FieldContext' ;
5
5
import Field from './Field' ;
6
- import { getNamePath , setValue } from './utils/valueUtil' ;
6
+ import { getNamePath } from './utils/valueUtil' ;
7
7
8
8
interface ListField {
9
9
name : number ;
@@ -21,85 +21,90 @@ interface ListProps {
21
21
}
22
22
23
23
const List : React . FunctionComponent < ListProps > = ( { name, children } ) => {
24
+ const context = React . useContext ( FieldContext ) ;
25
+ const keyRef = React . useRef ( {
26
+ keys : [ ] ,
27
+ id : 0 ,
28
+ } ) ;
29
+ const keyManager = keyRef . current ;
30
+
24
31
// User should not pass `children` as other type.
25
32
if ( typeof children !== 'function' ) {
26
33
warning ( false , 'Form.List only accepts function as children.' ) ;
27
34
return null ;
28
35
}
29
36
37
+ const parentPrefixName = getNamePath ( context . prefixName ) || [ ] ;
38
+ const prefixName : InternalNamePath = [ ...parentPrefixName , ...getNamePath ( name ) ] ;
39
+
40
+ const shouldUpdate = ( prevValue : StoreValue , nextValue : StoreValue , { source } ) => {
41
+ if ( source === 'internal' ) {
42
+ return false ;
43
+ }
44
+ return prevValue !== nextValue ;
45
+ } ;
46
+
30
47
return (
31
- < FieldContext . Consumer >
32
- { ( context : InternalFormInstance ) => {
33
- const parentPrefixName = getNamePath ( context . prefixName ) || [ ] ;
34
- const prefixName : InternalNamePath = [ ...parentPrefixName , ...getNamePath ( name ) ] ;
35
-
36
- const shouldUpdate = ( prevValue : StoreValue , nextValue : StoreValue , { source } ) => {
37
- if ( source === 'internal' ) {
38
- return false ;
39
- }
40
- return prevValue !== nextValue ;
41
- } ;
42
-
43
- return (
44
- < FieldContext . Provider value = { { ...context , prefixName } } >
45
- < Field name = { [ ] } shouldUpdate = { shouldUpdate } >
46
- { ( { value = [ ] , onChange } ) => {
47
- const { getInternalHooks, getFieldValue, setFieldsValue, setFields } = context ;
48
-
49
- /**
50
- * Always get latest value in case user update fields by `form` api.
51
- */
52
- const operations : ListOperations = {
53
- add : ( ) => {
54
- const newValue = ( getFieldValue ( prefixName ) || [ ] ) as StoreValue [ ] ;
55
- onChange ( [ ...newValue , undefined ] ) ;
56
- } ,
57
- remove : ( index : number ) => {
58
- const { getFields } = getInternalHooks ( HOOK_MARK ) ;
59
- const newValue = ( getFieldValue ( prefixName ) || [ ] ) as StoreValue [ ] ;
60
- const namePathList : InternalNamePath [ ] = newValue . map ( ( __ , i ) => [
61
- ...prefixName ,
62
- i ,
63
- ] ) ;
64
-
65
- const fields = getFields ( namePathList )
66
- . filter ( ( __ , i ) => i !== index )
67
- . map ( ( fieldData , i ) => ( {
68
- ...fieldData ,
69
- name : [ ...prefixName , i ] ,
70
- } ) ) ;
71
-
72
- const nextValue = [ ...newValue ] ;
73
- nextValue . splice ( index , 1 ) ;
74
-
75
- setFieldsValue ( setValue ( { } , prefixName , [ ] ) ) ;
76
-
77
- // Set value back.
78
- // We should add current list name also to let it re-render
79
- setFields ( [
80
- ...fields ,
81
- {
82
- name : prefixName ,
83
- } ,
84
- ] ) ;
85
- } ,
86
- } ;
48
+ < FieldContext . Provider value = { { ...context , prefixName } } >
49
+ < Field name = { [ ] } shouldUpdate = { shouldUpdate } >
50
+ { ( { value = [ ] , onChange } ) => {
51
+ const { getFieldValue } = context ;
52
+
53
+ /**
54
+ * Always get latest value in case user update fields by `form` api.
55
+ */
56
+ const operations : ListOperations = {
57
+ add : ( ) => {
58
+ // Mapping keys
59
+ keyManager . keys = [ ...keyManager . keys , keyManager . id ] ;
60
+ keyManager . id += 1 ;
61
+
62
+ const newValue = ( getFieldValue ( prefixName ) || [ ] ) as StoreValue [ ] ;
63
+ onChange ( [ ...newValue , undefined ] ) ;
64
+ } ,
65
+ remove : ( index : number ) => {
66
+ const newValue = ( getFieldValue ( prefixName ) || [ ] ) as StoreValue [ ] ;
87
67
88
- return children (
89
- ( value as StoreValue [ ] ) . map (
90
- ( __ , index ) : ListField => ( {
91
- name : index ,
92
- key : index ,
93
- } ) ,
94
- ) ,
95
- operations ,
96
- ) ;
97
- } }
98
- </ Field >
99
- </ FieldContext . Provider >
100
- ) ;
101
- } }
102
- </ FieldContext . Consumer >
68
+ // Do not handle out of range
69
+ if ( index < 0 || index >= newValue . length ) {
70
+ return ;
71
+ }
72
+
73
+ // Update key mapping
74
+ const newKeys = keyManager . keys . map ( ( key , id ) => {
75
+ if ( id < index ) {
76
+ return key ;
77
+ }
78
+ return keyManager . keys [ id + 1 ] ;
79
+ } ) ;
80
+ keyManager . keys = newKeys . slice ( 0 , - 1 ) ;
81
+
82
+ // Trigger store change
83
+ onChange ( newValue . filter ( ( _ , id ) => id !== index ) ) ;
84
+ } ,
85
+ } ;
86
+
87
+ return children (
88
+ ( value as StoreValue [ ] ) . map (
89
+ ( __ , index ) : ListField => {
90
+ let key = keyManager . keys [ index ] ;
91
+ if ( key === undefined ) {
92
+ keyManager . keys [ index ] = keyManager . id ;
93
+ key = keyManager . keys [ index ] ;
94
+ keyManager . id += 1 ;
95
+ }
96
+
97
+ return {
98
+ name : index ,
99
+ key,
100
+ } ;
101
+ } ,
102
+ ) ,
103
+ operations ,
104
+ ) ;
105
+ } }
106
+ </ Field >
107
+ </ FieldContext . Provider >
103
108
) ;
104
109
} ;
105
110
0 commit comments