diff --git a/packages/uniwind/src/hoc/withUniwind.native.tsx b/packages/uniwind/src/hoc/withUniwind.native.tsx index 8453d3c3..b4d1cc95 100644 --- a/packages/uniwind/src/hoc/withUniwind.native.tsx +++ b/packages/uniwind/src/hoc/withUniwind.native.tsx @@ -1,5 +1,5 @@ import type { ComponentProps } from 'react' -import { useCallback, useSyncExternalStore } from 'react' +import { useCallback, useLayoutEffect, useReducer } from 'react' import type { StyleDependency } from '../common/consts' import { isDefined } from '../common/utils' import { useUniwindContext } from '../core/context' @@ -25,8 +25,15 @@ const useDependencies = (dependencies: Array) => { () => UniwindListener.getSnapshot(uniqueDependencies), [dependencySum], ) + const [snapshot, rerender] = useReducer(getSnapshot, undefined, getSnapshot) - useSyncExternalStore(subscribe, getSnapshot, getSnapshot) + useLayoutEffect(() => { + if (getSnapshot() !== snapshot) { + rerender() + } + + return subscribe(rerender) + }, [subscribe, getSnapshot]) } export const withUniwind: WithUniwind = < diff --git a/packages/uniwind/src/hoc/withUniwind.tsx b/packages/uniwind/src/hoc/withUniwind.tsx index 41154129..d7c07d8b 100644 --- a/packages/uniwind/src/hoc/withUniwind.tsx +++ b/packages/uniwind/src/hoc/withUniwind.tsx @@ -1,5 +1,5 @@ import type { ComponentProps } from 'react' -import { useCallback, useSyncExternalStore } from 'react' +import { useCallback, useLayoutEffect, useReducer } from 'react' import { isDefined } from '../common/utils' import { generateDataSet } from '../components/web/generateDataSet' import { useUniwindContext } from '../core/context' @@ -20,7 +20,15 @@ const useClassNames = (classNames: string) => { [classNames], ) - useSyncExternalStore(subscribe, getSnapshot, getSnapshot) + const [snapshot, rerender] = useReducer(getSnapshot, undefined, getSnapshot) + + useLayoutEffect(() => { + if (getSnapshot() !== snapshot) { + rerender() + } + + return subscribe(rerender) + }, [subscribe, getSnapshot]) } export const withUniwind: WithUniwind = < diff --git a/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts index 9f6f9925..454977eb 100644 --- a/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts +++ b/packages/uniwind/src/hooks/useCSSVariable/useCSSVariable.ts @@ -1,4 +1,4 @@ -import { useMemo, useRef, useSyncExternalStore } from 'react' +import { useLayoutEffect, useMemo, useReducer, useRef } from 'react' import { Platform } from 'react-native' import { StyleDependency } from '../../common/consts' import { arrayEquals } from '../../common/utils' @@ -93,5 +93,16 @@ export const useCSSVariable: GetCSSVariable = (name: string | Array) => } }, [stableName, uniwindContext]) - return useSyncExternalStore(subscribe, getSnapshot, getSnapshot) as never + const [snapshot, rerender] = useReducer(getSnapshot, undefined, getSnapshot) + const currentSnapshot = getSnapshot() + + useLayoutEffect(() => { + if (getSnapshot() !== snapshot) { + rerender() + } + + return subscribe(rerender) + }, [subscribe, getSnapshot]) + + return currentSnapshot as never } diff --git a/packages/uniwind/src/hooks/useUniwind.ts b/packages/uniwind/src/hooks/useUniwind.ts index 37f6ff74..f7cdda8b 100644 --- a/packages/uniwind/src/hooks/useUniwind.ts +++ b/packages/uniwind/src/hooks/useUniwind.ts @@ -1,4 +1,4 @@ -import { useSyncExternalStore } from 'react' +import { useLayoutEffect, useReducer } from 'react' import { StyleDependency } from '../common/consts' import { Uniwind } from '../core' import { useUniwindContext } from '../core/context' @@ -11,14 +11,28 @@ const subscribeToNothing = () => () => {} const getTheme = () => Uniwind.currentTheme const getHasAdaptiveThemes = () => Uniwind.hasAdaptiveThemes +const useSnapshot = (subscribe: (callback: () => void) => () => void, getSnapshot: () => T) => { + const [snapshot, rerender] = useReducer(getSnapshot, undefined, getSnapshot) + const currentSnapshot = getSnapshot() + + useLayoutEffect(() => { + if (getSnapshot() !== snapshot) { + rerender() + } + + return subscribe(rerender) + }, [subscribe, getSnapshot]) + + return currentSnapshot +} + export const useUniwind = (): { theme: ThemeName; hasAdaptiveThemes: boolean } => { const uniwindContext = useUniwindContext() const isScoped = uniwindContext.scopedTheme !== null - const theme = useSyncExternalStore(isScoped ? subscribeToNothing : subscribeToTheme, getTheme, getTheme) - const hasAdaptiveThemes = useSyncExternalStore( + const theme = useSnapshot(isScoped ? subscribeToNothing : subscribeToTheme, getTheme) + const hasAdaptiveThemes = useSnapshot( isScoped ? subscribeToNothing : subscribeToAdaptiveThemes, getHasAdaptiveThemes, - getHasAdaptiveThemes, ) return { diff --git a/packages/uniwind/tests/native/components/scoped-variables.test.tsx b/packages/uniwind/tests/native/components/scoped-variables.test.tsx index 75ce02d3..26c3b263 100644 --- a/packages/uniwind/tests/native/components/scoped-variables.test.tsx +++ b/packages/uniwind/tests/native/components/scoped-variables.test.tsx @@ -105,6 +105,33 @@ describe('ScopedVariables', () => { expect(inside).toHaveBeenCalledWith('#123456') }) + test('useCSSVariable reflects a changed variable name during render', () => { + const seen: Array = [] + const variables = { '--color-primary': '#3b82f6', '--color-secondary': '#ff0000' } + + const Probe = ({ name }: { name: string }) => { + seen.push(useCSSVariable(name)) + + return null + } + + const Wrapper = ({ name }: { name: string }) => ( + + + + ) + + const { rerender } = renderUniwind() + const renderCount = seen.length + + act(() => { + rerender() + }) + + expect(seen.slice(renderCount)).not.toContain('#3b82f6') + expect(seen.at(-1)).toEqual('#ff0000') + }) + test('useCSSVariable reflects an updated variables prop', () => { const seen: Array = []