Skip to content

fix(hooks): warn and no-op when a property setter is called after dispose - #316

Merged
mfazekas merged 2 commits into
mainfrom
fix/stale-setter-write
Jul 6, 2026
Merged

mfazekas merged 2 commits into
mainfrom
fix/stale-setter-write

Conversation

@mfazekas

@mfazekas mfazekas commented Jul 6, 2026 •

Copy link
Copy Markdown
Collaborator

A setter returned by useRiveNumber/useRiveString/etc. can be captured in a stale closure (async callback, timeout) and fire after its property was disposed by a deps change or unmount. The write then reaches the disposed hybrid and throws Cannot set hybrid property ... NativeState is null — fatal when uncaught in a release app.

The fix routes writes through the liveRef that useDisposableMemo already maintains (nulled at dispose time) — the same guard useRiveTrigger uses. A write after unmount warns and no-ops (with the path and likely cause) instead of throwing; a write captured before a deps change targets the hook's current property, matching useRiveTrigger semantics. property stays in the useCallback deps so the setter identity still changes with the property.

Tests: jest cases for the after-unmount no-op+warn and the deps-change redirect, plus an on-device harness test (staleSetterWrite.test.tsx) that fails on the unguarded hook — verified on the Android emulator on both backends.

@mfazekas
mfazekas requested a review from HayesGordon July 6, 2026 05:08
@mfazekas
mfazekas enabled auto-merge (squash) July 6, 2026 11:45

@HayesGordon HayesGordon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@mfazekas mfazekas changed the title fix(hooks): no-op stale property setter calls after dispose fix(hooks): warn and no-op when a property setter is called after dispose Jul 6, 2026
mfazekas added 2 commits July 6, 2026 14:11
A setter returned by useRiveNumber/useRiveString/etc. can be captured in a
stale closure (async callback, timeout) and fire after its property was
disposed by a deps change or unmount. The write then hits the disposed
hybrid and throws "Cannot set hybrid property ... NativeState is null" -
fatal when uncaught in release.

Route writes through the liveRef that useDisposableMemo already maintains
(nulled at dispose time), the same guard useRiveTrigger uses, so stale
writes become safe no-ops. Adds jest regression tests plus an on-device
harness test that fails on the unguarded hook.
Mirror useRiveTrigger: instead of silently ignoring a setter call on a
missing property, warn with the path and the likely cause (stale closure
after unmount vs. instance still loading) so misuse stays visible without
crashing. Also reformat with prettier 3.8.3 (the version main resolves;
CI lint failed on formatting produced by 3.6.2).
@mfazekas
mfazekas force-pushed the fix/stale-setter-write branch from cc01bda to 24737dc Compare July 6, 2026 12:11
@mfazekas
mfazekas merged commit dbb4dd7 into main Jul 6, 2026
9 checks passed
@mfazekas
mfazekas deleted the fix/stale-setter-write branch July 6, 2026 12:20
mfazekas pushed a commit that referenced this pull request Jul 6, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.4.14](v0.4.13...v0.4.14)
(2026-07-06)


### Bug Fixes

* **android:** bump rive-android to 11.6.2
([#317](#317))
([ae66b24](ae66b24))
* **android:** don't re-acquire a disposed Rive file on view re-attach
([#319](#319))
([4259723](4259723))
* **hooks:** no-op stale property setter calls after dispose
([#316](#316))
([dbb4dd7](dbb4dd7))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
mfazekas added a commit that referenced this pull request Jul 7, 2026
Merges `main` up to the 0.4.14 release into the iOS experimental branch.
No conflicts.

Notable changes pulled in:
1. fix(android): don't re-acquire a disposed Rive file on view re-attach
(#319)
2. fix(hooks): no-op stale property setter calls after dispose (#316)
3. fix(android): bump rive-android to 11.6.2 (#317) — resolves to 11.6.2
here (up from 11.6.1)

On the #319 native fix: this branch has the legacy/new Android backend
split, so git's rename detection applied the fix to the
`android/src/legacy` variant of `RiveReactNativeView.kt` — which is
correct, since the crash is a property of upstream `RiveAnimationView`'s
cached `rendererAttributes.resource`. The `android/src/new` backend is a
separate `TextureView`/`CommandQueue` implementation with no such code
path, so the same tweak neither applies nor is needed there. A re-attach
lifecycle bug in the new backend, if any, would be a separate fix.

---------

Co-authored-by: Rive Engineering <60802183+rive-engineering@users.noreply.github.com>
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.

2 participants