Skip to content

OSS built-in styles stay stale after Activity or Suspense reconnects unchanged children #689

Description

@eliotgevers

What happened?

Built-in Uniwind components can retain stale className styles after their layout effects reconnect, until an unrelated update causes them to render again.

For example, a memoized tab starts in light mode, is hidden with React Activity, the theme changes to dark, and the tab is shown again. Its internal state is preserved, but its text and panel can still use the light colors. Accent colors resolved through the same hook are affected too.

Expected: restored content keeps its state and reflects the current theme.

Actual: restored content keeps its state but can display the previous theme indefinitely. A subsequent theme change while visible repairs it.

Steps to Reproduce

  1. Install the pinned dependencies and launch the standalone Expo app.
  2. Start in light mode and increment the saved counter.
  3. Press Hide tab, Toggle theme, then Show tab.
  4. The runtime theme reads dark, but the memoized card retains the light palette. The saved counter is retained.
  5. Repeat with a system appearance change while following system theme.

The reproduction uses React, React Native, Expo, and OSS Uniwind. It does not require navigation, HeroUI, a backend, React Compiler, or Uniwind Pro. Explicit memoization reproduces the stable-child boundary that a compiler can also produce.

Source-level cause

packages/uniwind/src/components/native/useStyle.ts computes styles during render. Its layout effect only subscribes to subsequent changes. React may reconnect that effect without rendering unchanged children, so changes that happened during disconnection are never reconciled.

Capturing the dependency revision at render and checking it immediately after subscribing closes this gap. The candidate adds seven lines and uses the existing listener revision counter. It does not keep hidden component subscriptions active or remount tab content.

Snack or Repository Link

https://github.com/eliotgevers/uniwind-activity-reproduction

Uniwind version

1.12.0

React Native Version

0.88.0-rc.2 (iOS simulator reproduction); 0.86.0 (native Jest regressions)

Platforms

iOS

Expo

Yes

Environment and verification

  • Uniwind: 1.12.0; current main c7978fd has the same affected hook.
  • React Native regression tests: React 19.2.3 / RN 0.86.0.
  • Lifecycle matrix: React DOM 19.2.3 and 19.3.0 with the real native style store/listener and stubbed RN environment constants. This supports lifecycle attribution, not native rendering claims.
  • iOS 27.0 simulator: Expo 58.0.0-preview.4 / React 19.3.0 / RN 0.88.0-rc.2. Stock fails both hidden explicit theme directions, OS light-to-dark while hidden, and a hidden variable update; candidate passes the same sequence and visible/display:none controls.
  • No Android device claim is made.

Proposed fix with two focused native regressions: #690. No public API changes.

Additional information 〰

  • Searched existing issues and PRs. This report isolates the current OSS native useStyle behavior with stable children and includes an independent reproduction.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions