Skip to content

fix(ios): force-apply all view props on a component view's first updateProps (blank view after react-freeze recreation) - #1484

Closed
rlods wants to merge 1 commit into
margelo:mainfrom
rlods:fix/force-apply-props-on-fresh-component-view
Closed

rlods wants to merge 1 commit into
margelo:mainfrom
rlods:fix/force-apply-props-on-fresh-component-view

Conversation

@rlods

@rlods rlods commented Aug 18, 2026

Copy link
Copy Markdown

Symptom

On iOS (new architecture), a Nitro view renders permanently blank after navigating away from its screen and coming back, when the app uses react-native-screens with enableFreeze(true) (react-freeze / Suspense). The native view is laid out correctly (size, background) but no props are ever applied to the fresh hybrid, and hybridRef never fires — so JS keeps a ref to the dead old hybrid and imperative calls silently no-op.

Originally reproduced and analyzed with RiveView from rive-app/rive-nitro-react-native — full write-up in rive-app/rive-nitro-react-native#365 (opened against the generated file committed there; this PR is the durable fix in the template, as suggested in that PR's description).

Root cause

When a screen is frozen, Fabric deletes its native views; on return it recreates them from the same, unchanged ShadowNodes — calling updateProps:oldProps: on the brand-new component view with the same cached Props object as before.

The generated updateProps guards every prop with isDirty and, after applying it, mutates the shared props object (newViewProps.<prop>.isDirty = false; via const_cast). Those flags were already consumed by the previous view instance, so on recreation updateProps applies nothing:

  • the fresh HybridView keeps all its default values (unconfigured);
  • hybridRef never re-fires, so JS still holds the ref to the old, dead hybrid.

Nothing ever marks the props dirty again unless a prop changes identity on the JS side, so the view stays broken forever.

The same "consumed flags on a shared Props object" mechanism also affects recycled views being remounted for a different ShadowNode — the stale-props family of #1050.

Fix

In SwiftHybridViewManager.ts (the template for Hybrid*Component.mm):

  • track BOOL _didApplyInitialProps on the component view;
  • force-apply every prop (and hybridRef) on the first updateProps of each view instance, keeping the isDirty fast path for all subsequent updates;
  • reset the flag in prepareForRecycle, so a recycled view force-applies the next node's props on remount.

First-ever mounts are unaffected (all flags are dirty there anyway). Regenerated the committed react-native-nitro-test fixtures.

We've been running the equivalent patch (minus the recycle part) in production (several Rive scenes behind frozen tab/stack screens) and it fixes the blank-view repro deterministically.

Android's JNI state updater has the same consumed-isDirty pattern in theory, but we could not reproduce the bug there and fixing it needs a JNI signature change (updateViewProps(view, state, forceApply)), so this PR is iOS-only — happy to follow up on Android if you think it's worth it.

Repro

  1. RN app with new architecture + react-native-screens, enableFreeze(true)
  2. Screen A renders a Nitro view with props; push any screen B on top (freeze unmounts A's native views)
  3. Go back to A → the recreated view has no props applied; hybridRef never fired; only a prop identity change revives it

🤖 Generated with Claude Code

…teProps

Fabric can mount a component view with a Props object whose isDirty flags
were already consumed by a different view instance (view recreated from an
unchanged ShadowNode, e.g. react-freeze; or a recycled view remounted for
another node). updateProps would then apply nothing, leaving the fresh
HybridView unconfigured and hybridRef never firing.

Track _didApplyInitialProps per view instance, force-apply everything on
the first updateProps, and reset the flag in prepareForRecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
nitro-docs Skipped Skipped Aug 18, 2026 1:36pm

Request Review

@rlods

rlods commented Aug 18, 2026

Copy link
Copy Markdown
Author

Closing as duplicate of #1479, which handles never-set props (hasValue()), covers Android, and adds a regression test.

@rlods rlods closed this Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant