useDisposableMemo disposes native objects in effect cleanup, so a subtree hidden by React <Activity> crashes in release builds when shown again

未关闭
#448 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

@mfazekas 已经在做这个了。

开始于 2026年11月6日。

  • #449 来自 @mfazekas —— 未关闭

评估

难度
4/5
预计耗时
3-5 天
新手友好度
48/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
react, react-native, typescript
领域
backend, mobile

调研方向

Start with src/hooks/useDisposableMemo.ts and trace how its effect cleanup disposes the memoized native value. Reproduce the hide and reveal behavior with React <Activity mode="hidden"> in production, then check the existing tests for this hook. Done means revealing the subtree leaves the value usable, with a regression test covering the lifecycle.

由索引模型根据 Issue 内容生成。

描述

Summary

React's <Activity mode="hidden"> runs a component's effect cleanups without unmounting it, and on reveal re-runs the effects with the same memoised values. In production builds useDisposableMemo disposes its value in that effect cleanup. After a hide and reveal the component still holds the disposed HybridObject, and the next call on it dereferences null in native code: release builds skip Nitro's null-NativeState check. Any app that hides a subtree containing a Rive view with <Activity> (React 19.2+) can hit this. We do it for inactive screens and for an animated mascot.

Versions
  • @rive-app/react-native 0.5.5 (RiveRuntime iOS 6.28.1, rive-android 11.12.1). src/hooks/useDisposableMemo.ts on main has the same cleanup.
  • react-native-nitro-modules 0.36.5, React Native 0.88.0, React 19.3.0, Expo SDK 58, new architecture, Hermes.
What we see (production, about 8 days)
iOS Android
Crashes / users 35 / 35 38 / 37
OS iOS 17.6 – 27.0.1 Android 11 – 17
Devices iPhones and iPads moto g35 / g85, Lenovo Tab TB330FU / TB328FU, Samsung A05 / A16 / A52s, vivo, Xiaomi

Each crash is the first call on a view-model instance, or on a property taken from it, after the subtree is shown again. Breadcrumbs show a navigation back to the screen just before.

iOS (EXC_BAD_ACCESS, address 0, JS thread)
hermes::vm::NativeFunction::_nativeCall
margelo::nitro::HybridFunction::createHybridFunction<…>::lambda::operator() (HybridFunction.hpp:119)
margelo::nitro::HybridFunction::callMethod<…> (HybridFunction.hpp:175)

Android (SIGSEGV)
facebook::react::RuntimeScheduler_Modern::executeTask
facebook::jsi::Function::call
margelo::nitro::HybridFunction::createHybridFunction<…>::lambda::operator() (HybridFunction.hpp:119)
margelo::nitro::HybridFunction::callMethod<…> (HybridFunction.hpp:179)
Root cause
useEffect(() => {
  …
  return () => {
    if (__DEV__) { /* deferred with setTimeout(0), cancelled if the effect re-runs */ }
    else {
      cleanupRef.current(ref.current.value);   // disposes now
    }
  };
}, []);
return ref.current.value;                      // the same, now dead, value after the reveal

The __DEV__ branch survives Strict Mode and Fast Refresh because the re-run effect cancels the pending disposal. The production branch has no equivalent, and <Activity> reaches it in production. This is the mechanism of #407 (fixed for useRiveList in #408), but in the shared hook, so it reaches useViewModelInstance and every hook built on it.

Suggested fix

Don't dispose in the effect cleanup. Dispose when the deps change (already done during render), and on a real unmount let the native object go when the JS wrapper is collected. Alternatives:

  • keep the dev-style deferred disposal in production, cancelled when the effect re-runs;
  • mark the value stale in the cleanup so the next render creates a fresh one.

We carry the first option as a local patch (not released yet), with a unit test of the hide-and-reveal case.

Related (cause not confirmed)

After the same hide and reveal, riveViewRef from useRive() is still non-null, but riveViewRef.play / .pause are undefined: "TypeError: undefined is not a function" when an effect that <Activity> re-runs calls them. We saw about 2,450 of these from about 750 users before guarding the calls on our side. It might help to clear the ref, or keep its methods callable, while the view is hidden.

主要语言
TypeScript
星标
158
派生
17
平均合并
4 天 4 小时
30 天内合并 PR
23

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

rive-app/rive-nitro-react-native 的其他 Issue

查看 rive-app/rive-nitro-react-native 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。