diff --git a/packages/uniwind/src/components/ScopedVariables/utils.ts b/packages/uniwind/src/components/ScopedVariables/utils.ts
index 7705e112..bff7ff59 100644
--- a/packages/uniwind/src/components/ScopedVariables/utils.ts
+++ b/packages/uniwind/src/components/ScopedVariables/utils.ts
@@ -30,8 +30,9 @@ export const buildScopedVariablesContext = (
...validateVariables(variables),
} as UniwindContextCSSVariables
delete mergedVariables.__uniwindVariablesCacheKey
+ // The key only needs a stable order. Code-unit order avoids localeCompare, which creates a collator per call on Android
const variablesCacheKey = JSON.stringify(
- Object.entries(mergedVariables).sort(([a], [b]) => a.localeCompare(b)),
+ Object.entries(mergedVariables).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0),
)
mergedVariables.__uniwindVariablesCacheKey = variablesCacheKey
diff --git a/packages/uniwind/tests/native/components/scoped-variables.test.tsx b/packages/uniwind/tests/native/components/scoped-variables.test.tsx
index 26c3b263..88eb8315 100644
--- a/packages/uniwind/tests/native/components/scoped-variables.test.tsx
+++ b/packages/uniwind/tests/native/components/scoped-variables.test.tsx
@@ -311,6 +311,38 @@ describe('ScopedVariables', () => {
expect(ambiguous.mock.calls[0][0]).not.toEqual(plain.mock.calls[0][0])
})
+ test('cache key ignores variable order without locale collation', () => {
+ const localeCompare = jest.spyOn(String.prototype, 'localeCompare')
+ const Probe = (props: { test: jest.Mock }) => {
+ props.test(useUniwindContext().variables?.__uniwindVariablesCacheKey)
+
+ return null
+ }
+
+ const first = jest.fn()
+ const second = jest.fn()
+
+ try {
+ renderUniwind(
+
+
+
+
+
+
+
+ ,
+ )
+
+ expect(first).toHaveBeenCalledWith('[["--color-primary","#ff0000"],["--gap",8]]')
+ expect(second).toHaveBeenCalledWith('[["--color-primary","#ff0000"],["--gap",8]]')
+ // Hermes on Android creates a collator for every localeCompare call
+ expect(localeCompare).not.toHaveBeenCalled()
+ } finally {
+ localeCompare.mockRestore()
+ }
+ })
+
test('updating the variables prop does not serve stale cached styles', () => {
const Wrapper = ({ color }: { color: string }) => (