feat(react-media-store): add shallowEqual utility for optimized state comparisons

- Add shallowEqual function with array-specific optimizations to MediaProvider
- Export refEquality function for external use
- Export shallowEqual from package index for consumers

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Christian Pillsbury
2025-09-08 17:15:40 -07:00
committed by Christian Pillsbury
co-authored by Claude
parent ad7aa79b61
commit 8e75d62f62
2 changed files with 60 additions and 5 deletions
@@ -64,7 +64,61 @@ export const useMediaRef = () => {
};
};
const refEquality = (a: any, b: any) => a === b;
export const refEquality = (a: any, b: any) => a === b;
const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Slightly modified version of React's shallowEqual, with optimizations for Arrays
* so we may treat them specifically as unequal if they are not a) both arrays
* or b) don't contain the same (shallowly compared) elements.
*/
export const shallowEqual = (objA: any, objB: any): boolean => {
// Using Object.is as a first pass, as it covers a lot of the "simple" cases that are
// more complex than strict equality and is a built-in. For discussion, see, e.g.:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#description
if (Object.is(objA, objB)) {
return true;
}
// Since we've done an Object.is() check immediately above, we can safely assume non-objects (or null-valued objects)
// are not equal, so can early bail for those as well.
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}
if (Array.isArray(objA)) {
// Early "cheap" array compares
if (!Array.isArray(objB) || objA.length !== objB.length) return false;
// Shallow compare for arrays
return objA.some((vVal, i) => objB[i] === vVal);
}
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
// NOTE: Since we've already guaranteed the keys list lengths are the same, we can safely cast to string here (CJP)
if (
!hasOwnProperty.call(objB, keysA[i] as string) ||
!Object.is(objA[keysA[i] as string], objB[keysA[i] as string])
) {
return false;
}
}
return true;
};
export const useMediaSelector = <S = any,>(
selector: (state: any) => S,
@@ -1,14 +1,15 @@
// Re-export everything from MediaProvider - this is the primary implementation
export {
MediaProvider,
export {
MediaProvider,
MediaContext,
useMediaStore,
useMediaDispatch,
useMediaRef,
useMediaSelector
useMediaSelector,
shallowEqual,
} from './MediaProvider.js';
// @ts-ignore - Placeholder types until media-store exports are updated
export type MediaStore = any;
export type MediaState = any;
export type MediaStateOwner = any;
export type MediaStateOwner = any;