callstack / callstack/react-native-brownfield

[Android] ReactNativeFragment leaks OnBackPressedCallback, breaking back after re-entry

Đang mở
#457 1 bình luận 0 reaction 1 người được giao Được @likevy nhận Xem trên GitHub
Ngôn ngữ chính
TypeScript
Star
547
Fork
52
Merge trung bình
3 ngày 16 giờ
Pull request đã merge (30 ngày)
5

Mô tả

### Summary

`ReactNativeBrownfield.createView` adds an `OnBackPressedCallback` without a `LifecycleOwner` and never removes it. `ReactNativeFragment.onCreateView` calls `createView` for each new view, so callbacks from destroyed views remain enabled for the lifetime of the activity.

In a single-activity host, back works on the first visit to the React Native screen. After leaving and reopening it, hardware/gesture back stops working.

### Environment

| | |
| --- | --- |
| `@callstack/react-native-brownfield` | 5.1.0 |
| React Native | 0.87.0 |
| Android | API 35 emulator |
| Host | Single activity; `ReactNativeFragment` is directly on the activity fragment back stack |

### Minimal reproduction

[findoo/react-native-brownfield-back-press-repro](https://github.com/findoo/react-native-brownfield-back-press-repro)

The reproduction reduces this repository's Android example to one `AppCompatActivity`, one native fragment, one `ReactNativeFragment`, and a React Native component containing only `Text`. It has no JavaScript navigation stack or `BackHandler`.

1. Tap **Open React Native**.
2. Press hardware or gesture back. The native fragment returns.
3. Tap **Open React Native** again.
4. Press back again.

Expected: the native fragment returns.

Actual: the React Native fragment remains visible. Further visits add further callbacks, and back remains broken until the activity is recreated.

https://github.com/user-attachments/assets/7c60a9af-38c7-47d1-8c03-6275d89842ac

### Cause

[`createView`](https://github.com/callstack/react-native-brownfield/blob/bfd88af993733450c7f3a74d829f81c63120fac1/packages/react-native-brownfield/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeBrownfield.kt#L200-L234) registers the callback using the non-lifecycle-aware overload:

```kotlin
activity?.onBackPressedDispatcher?.addCallback(backPressedCallback)
```

That overload requires explicit removal, but no removal exists. [`ReactNativeFragment.onCreateView`](https://github.com/callstack/react-native-brownfield/blob/bfd88af993733450c7f3a74d829f81c63120fac1/packages/react-native-brownfield/android/src/main/java/com/callstack/reactnativebrownfield/ReactNativeFragment.kt#L49-L59) calls `createView` again for every view creation.

When the active callback disables itself to pass an unhandled press to the dispatcher, a callback retained from the previous view receives it instead. The dispatcher therefore never reaches the host fragment manager.

The lifecycle observer at lines 232–233 does not cover this: `ReactNativeFragment` supplies a delegate, so that branch is skipped, and the observer does not remove the callback in any case.

### Expected behaviour

Callbacks belonging to destroyed fragment views should not receive back presses. Back should behave the same on every visit.

### Related

- #227 fixes a callback not being re-enabled after forwarding a back press. This issue concerns callbacks from destroyed views not being removed.
- #354 concerns multiple React Navigation stacks on iOS. This reproduction is Android-only and has no JavaScript navigation stack.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.