@@ -248,3 +248,102 @@ export const useForceReRender = () => {
248248
249249 return forceUpdate ;
250250} ;
251+
252+ /**
253+ * Fast deep equality comparison function
254+ * Based on fast-deep-equal library
255+ * Note: Functions are treated as equal without deep comparison
256+ */
257+ export function isEqual ( a : any , b : any ) : boolean {
258+ if ( a === b ) return true ;
259+
260+ // Ignore function checks - return them as equal
261+ if ( typeof a === 'function' && typeof b === 'function' ) return true ;
262+
263+ if ( a && b && typeof a === 'object' && typeof b === 'object' ) {
264+ if ( a . constructor !== b . constructor ) return false ;
265+
266+ let length : number ;
267+ let i : any ;
268+ let keys : string [ ] ;
269+
270+ if ( Array . isArray ( a ) ) {
271+ length = a . length ;
272+ if ( length !== b . length ) return false ;
273+ for ( i = length ; i -- !== 0 ; ) {
274+ if ( ! isEqual ( a [ i ] , b [ i ] ) ) return false ;
275+ }
276+ return true ;
277+ }
278+
279+ if ( a instanceof Map && b instanceof Map ) {
280+ if ( a . size !== b . size ) return false ;
281+ for ( i of a . entries ( ) ) {
282+ if ( ! b . has ( i [ 0 ] ) ) return false ;
283+ }
284+ for ( i of a . entries ( ) ) {
285+ if ( ! isEqual ( i [ 1 ] , b . get ( i [ 0 ] ) ) ) return false ;
286+ }
287+ return true ;
288+ }
289+
290+ if ( a instanceof Set && b instanceof Set ) {
291+ if ( a . size !== b . size ) return false ;
292+ for ( i of a . entries ( ) ) {
293+ if ( ! b . has ( i [ 0 ] ) ) return false ;
294+ }
295+ return true ;
296+ }
297+
298+ if ( ArrayBuffer . isView ( a ) && ArrayBuffer . isView ( b ) ) {
299+ const aView = a as any ;
300+ const bView = b as any ;
301+ length = aView . length ;
302+ if ( length !== bView . length ) return false ;
303+ for ( i = length ; i -- !== 0 ; ) {
304+ if ( aView [ i ] !== bView [ i ] ) return false ;
305+ }
306+ return true ;
307+ }
308+
309+ if ( a . constructor === RegExp ) {
310+ return a . source === b . source && a . flags === b . flags ;
311+ }
312+
313+ if ( a . valueOf !== Object . prototype . valueOf ) {
314+ return a . valueOf ( ) === b . valueOf ( ) ;
315+ }
316+
317+ if ( a . toString !== Object . prototype . toString ) {
318+ return a . toString ( ) === b . toString ( ) ;
319+ }
320+
321+ keys = Object . keys ( a ) ;
322+ length = keys . length ;
323+ if ( length !== Object . keys ( b ) . length ) return false ;
324+
325+ for ( i = length ; i -- !== 0 ; ) {
326+ const key = keys [ i ] ;
327+ if ( key !== undefined && ! Object . prototype . hasOwnProperty . call ( b , key ) ) return false ;
328+ }
329+
330+ for ( i = length ; i -- !== 0 ; ) {
331+ const key = keys [ i ] ;
332+ if ( key === undefined ) continue ;
333+
334+ // React-specific: avoid traversing React elements' _owner
335+ // _owner contains circular references and is not needed when comparing actual elements
336+ if ( key === '_owner' && a . $$typeof ) {
337+ continue ;
338+ }
339+
340+ if ( ! isEqual ( a [ key ] , b [ key ] ) ) return false ;
341+ }
342+
343+ return true ;
344+ }
345+
346+ // true if both NaN, false otherwise
347+ return a !== a && b !== b ;
348+ }
349+
0 commit comments