@@ -4,13 +4,33 @@ import { isObjectOrArray } from 'react-form-simple/utils/util';
44
55export type ObserverOptions = {
66 path ?: string [ ] ;
7+ // 添加对象路径追踪,解决动态数组重排序问题
8+ pathTracker ?: WeakMap < any , string > ;
9+ // 根代理引用
10+ rootProxy ?: any ;
11+ // 是否强制重新创建代理(用于 reset 场景)
12+ forceRecreate ?: boolean ;
713} ;
814
915export type ObserverCb = { path : string ; value : any } ;
1016
17+ // 用于存储对象到代理的映射
18+ const objectToProxyMap = new WeakMap < any , any > ( ) ;
19+ // 用于存储代理到原始对象的映射
20+ const proxyToObjectMap = new WeakMap < any , any > ( ) ;
21+
1122export 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+ // 然后设置新值
1232 Object . entries ( values ) . forEach ( ( [ key , value ] ) => {
13- updateProxyValue ( proxyObject , key , value ) ;
33+ proxyObject [ key ] = value ;
1434 } ) ;
1535
1636 return proxyObject ;
@@ -35,6 +55,57 @@ export const getProxyValue = (
3555 return currentObj ;
3656} ;
3757
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+
38109export const updateProxyValue = (
39110 obj : any ,
40111 path : string ,
@@ -54,15 +125,48 @@ export const updateProxyValue = (
54125
55126 if ( current [ key ] === undefined ) {
56127 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+ }
58135 } else {
59136 return false ;
60137 }
61138 }
62139
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' ) {
64163 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+ }
66170 } else {
67171 return false ;
68172 }
@@ -72,7 +176,17 @@ export const updateProxyValue = (
72176 }
73177
74178 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 ) {
76190 current [ lastKey ] = newValue ;
77191 return true ;
78192 }
@@ -85,29 +199,108 @@ export const observer = <T extends DefaultRecord>(
85199 cb ?: ( args : ObserverCb ) => void ,
86200 options ?: ObserverOptions ,
87201) : 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 ;
89215
90216 const proxy = new Proxy ( initialVal , {
91217 get ( target , key , receiver ) {
92218 const ret = Reflect . get ( target , key , receiver ) ;
93219 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 ;
100249 } ,
101250 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+ }
103260
104261 const ret = Reflect . set ( target , key , val ) ;
105262
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+ }
107285
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 } ) ;
108297 return ret ;
109298 } ,
110299 } ) ;
111300
301+ // 建立双向映射
302+ objectToProxyMap . set ( initialVal , proxy ) ;
303+ proxyToObjectMap . set ( proxy , initialVal ) ;
304+
112305 return proxy ;
113306} ;
0 commit comments