@@ -4,13 +4,33 @@ import { isObjectOrArray } from 'react-form-simple/utils/util';
4
4
5
5
export type ObserverOptions = {
6
6
path ?: string [ ] ;
7
+ // 添加对象路径追踪,解决动态数组重排序问题
8
+ pathTracker ?: WeakMap < any , string > ;
9
+ // 根代理引用
10
+ rootProxy ?: any ;
11
+ // 是否强制重新创建代理(用于 reset 场景)
12
+ forceRecreate ?: boolean ;
7
13
} ;
8
14
9
15
export type ObserverCb = { path : string ; value : any } ;
10
16
17
+ // 用于存储对象到代理的映射
18
+ const objectToProxyMap = new WeakMap < any , any > ( ) ;
19
+ // 用于存储代理到原始对象的映射
20
+ const proxyToObjectMap = new WeakMap < any , any > ( ) ;
21
+
11
22
export const replaceTarget = ( proxyObject : any , values : any ) => {
23
+ // 简单方式:直接替换属性,让代理系统自己处理
24
+ // 先删除不在新值中的属性
25
+ Object . keys ( proxyObject ) . forEach ( ( key ) => {
26
+ if ( ! ( key in values ) ) {
27
+ delete proxyObject [ key ] ;
28
+ }
29
+ } ) ;
30
+
31
+ // 然后设置新值
12
32
Object . entries ( values ) . forEach ( ( [ key , value ] ) => {
13
- updateProxyValue ( proxyObject , key , value ) ;
33
+ proxyObject [ key ] = value ;
14
34
} ) ;
15
35
16
36
return proxyObject ;
@@ -35,6 +55,57 @@ export const getProxyValue = (
35
55
return currentObj ;
36
56
} ;
37
57
58
+ // 获取对象在当前代理中的实际路径
59
+ const getActualPath = ( rootProxy : any , target : any ) : string [ ] => {
60
+ if ( ! rootProxy || ! target ) return [ ] ;
61
+
62
+ const findPath = (
63
+ current : any ,
64
+ searchTarget : any ,
65
+ path : string [ ] = [ ] ,
66
+ ) : string [ ] | null => {
67
+ if ( current === searchTarget ) {
68
+ return path ;
69
+ }
70
+
71
+ if ( current && typeof current === 'object' ) {
72
+ // 获取原始对象进行比较
73
+ const originalCurrent = proxyToObjectMap . get ( current ) || current ;
74
+
75
+ if ( Array . isArray ( originalCurrent ) ) {
76
+ for ( let i = 0 ; i < originalCurrent . length ; i ++ ) {
77
+ const item = originalCurrent [ i ] ;
78
+ if ( item === searchTarget ) {
79
+ return [ ...path , i . toString ( ) ] ;
80
+ }
81
+ if ( item && typeof item === 'object' ) {
82
+ const result = findPath ( item , searchTarget , [
83
+ ...path ,
84
+ i . toString ( ) ,
85
+ ] ) ;
86
+ if ( result ) return result ;
87
+ }
88
+ }
89
+ } else {
90
+ for ( const [ key , value ] of Object . entries ( originalCurrent ) ) {
91
+ if ( value === searchTarget ) {
92
+ return [ ...path , key ] ;
93
+ }
94
+ if ( value && typeof value === 'object' ) {
95
+ const result = findPath ( value , searchTarget , [ ...path , key ] ) ;
96
+ if ( result ) return result ;
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ return null ;
103
+ } ;
104
+
105
+ const originalRoot = proxyToObjectMap . get ( rootProxy ) || rootProxy ;
106
+ return findPath ( originalRoot , target ) || [ ] ;
107
+ } ;
108
+
38
109
export const updateProxyValue = (
39
110
obj : any ,
40
111
path : string ,
@@ -54,15 +125,48 @@ export const updateProxyValue = (
54
125
55
126
if ( current [ key ] === undefined ) {
56
127
if ( options . createPath ) {
57
- current [ key ] = { } ;
128
+ // 检查下一个key是否为数字,如果是则创建数组
129
+ const nextKey = keys [ i + 1 ] ;
130
+ if ( ! isNaN ( Number ( nextKey ) ) ) {
131
+ current [ key ] = [ ] ;
132
+ } else {
133
+ current [ key ] = { } ;
134
+ }
58
135
} else {
59
136
return false ;
60
137
}
61
138
}
62
139
63
- if ( typeof current [ key ] !== 'object' || current [ key ] === null ) {
140
+ // 处理类型不匹配的情况
141
+ if ( current [ key ] !== null && typeof current [ key ] === 'object' ) {
142
+ // 检查是否需要从对象转为数组或从数组转为对象
143
+ const nextKey = keys [ i + 1 ] ;
144
+ const isNextKeyNumeric = ! isNaN ( Number ( nextKey ) ) ;
145
+ const currentIsArray = Array . isArray ( current [ key ] ) ;
146
+
147
+ if ( isNextKeyNumeric && ! currentIsArray ) {
148
+ // 需要转为数组
149
+ if ( options . createPath ) {
150
+ current [ key ] = [ ] ;
151
+ } else {
152
+ return false ;
153
+ }
154
+ } else if ( ! isNextKeyNumeric && currentIsArray ) {
155
+ // 需要转为对象
156
+ if ( options . createPath ) {
157
+ current [ key ] = { } ;
158
+ } else {
159
+ return false ;
160
+ }
161
+ }
162
+ } else if ( current [ key ] === null || typeof current [ key ] !== 'object' ) {
64
163
if ( options . createPath ) {
65
- current [ key ] = { } ;
164
+ const nextKey = keys [ i + 1 ] ;
165
+ if ( ! isNaN ( Number ( nextKey ) ) ) {
166
+ current [ key ] = [ ] ;
167
+ } else {
168
+ current [ key ] = { } ;
169
+ }
66
170
} else {
67
171
return false ;
68
172
}
@@ -72,7 +176,17 @@ export const updateProxyValue = (
72
176
}
73
177
74
178
const lastKey = keys [ keys . length - 1 ] ;
75
- if ( options . forceUpdate || lastKey in current ) {
179
+ const lastKeyIndex = Number ( lastKey ) ;
180
+
181
+ // 如果是数组且key是数字索引
182
+ if ( Array . isArray ( current ) && ! isNaN ( lastKeyIndex ) ) {
183
+ // 确保数组有足够的长度
184
+ while ( current . length <= lastKeyIndex ) {
185
+ current . push ( undefined ) ;
186
+ }
187
+ current [ lastKeyIndex ] = newValue ;
188
+ return true ;
189
+ } else if ( options . forceUpdate || lastKey in current ) {
76
190
current [ lastKey ] = newValue ;
77
191
return true ;
78
192
}
@@ -85,29 +199,108 @@ export const observer = <T extends DefaultRecord>(
85
199
cb ?: ( args : ObserverCb ) => void ,
86
200
options ?: ObserverOptions ,
87
201
) : T => {
88
- const { path = [ ] } = ( options || { } ) as ObserverOptions ;
202
+ const {
203
+ path = [ ] ,
204
+ pathTracker = new WeakMap ( ) ,
205
+ rootProxy,
206
+ forceRecreate = false ,
207
+ } = ( options || { } ) as ObserverOptions ;
208
+
209
+ // 如果已经为这个对象创建过代理且不强制重创建,直接返回
210
+ if ( ! forceRecreate && objectToProxyMap . has ( initialVal ) ) {
211
+ return objectToProxyMap . get ( initialVal ) ;
212
+ }
213
+
214
+ const actualRootProxy = rootProxy || null ;
89
215
90
216
const proxy = new Proxy ( initialVal , {
91
217
get ( target , key , receiver ) {
92
218
const ret = Reflect . get ( target , key , receiver ) ;
93
219
if ( React . isValidElement ( ret ) ) return ret ;
94
- return isObjectOrArray ( ret )
95
- ? observer ( ret as T , cb , {
96
- ...( options as ObserverOptions ) ,
97
- path : [ ...path , key . toString ( ) ] ,
98
- } )
99
- : ret ;
220
+
221
+ if ( isObjectOrArray ( ret ) ) {
222
+ // 对于嵌套对象,检查是否已经有代理(除非强制重创建)
223
+ if ( ! forceRecreate && objectToProxyMap . has ( ret ) ) {
224
+ return objectToProxyMap . get ( ret ) ;
225
+ }
226
+
227
+ // 计算当前路径
228
+ let currentPath : string [ ] ;
229
+ if ( actualRootProxy && target !== initialVal ) {
230
+ // 动态计算实际路径
231
+ currentPath = getActualPath ( actualRootProxy , target ) ;
232
+ currentPath . push ( key . toString ( ) ) ;
233
+ } else {
234
+ currentPath = [ ...path , key . toString ( ) ] ;
235
+ }
236
+
237
+ const childProxy = observer ( ret as T , cb , {
238
+ ...( options as ObserverOptions ) ,
239
+ path : currentPath ,
240
+ pathTracker,
241
+ rootProxy : actualRootProxy || proxy ,
242
+ forceRecreate,
243
+ } ) ;
244
+
245
+ return childProxy ;
246
+ }
247
+
248
+ return ret ;
100
249
} ,
101
250
set ( target , key , val ) {
102
- const newPath = [ ...path , key . toString ( ) ] ;
251
+ let currentPath : string [ ] ;
252
+
253
+ // 动态计算当前设置操作的实际路径
254
+ if ( actualRootProxy ) {
255
+ currentPath = getActualPath ( actualRootProxy , target ) ;
256
+ currentPath . push ( key . toString ( ) ) ;
257
+ } else {
258
+ currentPath = [ ...path , key . toString ( ) ] ;
259
+ }
103
260
104
261
const ret = Reflect . set ( target , key , val ) ;
105
262
106
- cb ?.( { path : newPath . join ( '.' ) , value : val } ) ;
263
+ // 如果设置的是对象/数组,清理旧的代理映射
264
+ if ( isObjectOrArray ( val ) ) {
265
+ // 清理可能存在的旧映射
266
+ const oldVal = target [ key as keyof typeof target ] ;
267
+ if ( oldVal && objectToProxyMap . has ( oldVal ) ) {
268
+ const oldProxy = objectToProxyMap . get ( oldVal ) ;
269
+ objectToProxyMap . delete ( oldVal ) ;
270
+ proxyToObjectMap . delete ( oldProxy ) ;
271
+ }
272
+ }
273
+
274
+ cb ?.( { path : currentPath . join ( '.' ) , value : val } ) ;
275
+ return ret ;
276
+ } ,
277
+ deleteProperty ( target , key ) {
278
+ // 处理删除操作
279
+ const deletedValue = target [ key as keyof typeof target ] ;
280
+ if ( deletedValue && objectToProxyMap . has ( deletedValue ) ) {
281
+ const deletedProxy = objectToProxyMap . get ( deletedValue ) ;
282
+ objectToProxyMap . delete ( deletedValue ) ;
283
+ proxyToObjectMap . delete ( deletedProxy ) ;
284
+ }
107
285
286
+ const ret = Reflect . deleteProperty ( target , key ) ;
287
+
288
+ let currentPath : string [ ] ;
289
+ if ( actualRootProxy ) {
290
+ currentPath = getActualPath ( actualRootProxy , target ) ;
291
+ currentPath . push ( key . toString ( ) ) ;
292
+ } else {
293
+ currentPath = [ ...path , key . toString ( ) ] ;
294
+ }
295
+
296
+ cb ?.( { path : currentPath . join ( '.' ) , value : undefined } ) ;
108
297
return ret ;
109
298
} ,
110
299
} ) ;
111
300
301
+ // 建立双向映射
302
+ objectToProxyMap . set ( initialVal , proxy ) ;
303
+ proxyToObjectMap . set ( proxy , initialVal ) ;
304
+
112
305
return proxy ;
113
306
} ;
0 commit comments