callstack / callstack/react-native-brownfield
popToNative closes all React Native screens
- Dominant language
- TypeScript
- Stars
- 547
- Forks
- 52
- Avg merge
- 3d 16h
- Merged PRs (30d)
- 5
Description
Hello,
I've been using react-native-brownfield for a while now and recently we added a new module inside the RNApp project.
A module is a separate stack which comes with its own screens and logic. Normally these modules should be as isolated as possible one from another.
The way I implemented the module is by using the NavigationContainer and the Stack.Navigator from react-navigation.
For testing purposes I've made a new SwiftUI view that allows me to navigate to each module based on the initialModule prop, and from React Native I am passing the prop to NavigationContainer's initialRouteName prop.
Now, just like in the attached video, if I navigate on Home module, then on Settings [native screen], then on Contact [RN screen], and then press the back button [which calls popToNative], it will close all the screens. Basically popToNative closes all the React Native screens.
I am not sure if this is the expected behavior or if it's a bug. I would expect popToNative to close only the React Native screens that are on the top of the stack.
Here is the code I am using:
```swift
import Brownie
import ReactBrownfield
import SwiftUI
import UIKit
enum HomeDestination: Hashable {
case rnHome
case nativeSettings
case rnContact
}
@available(iOS 16.0, *)
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
ZStack(alignment: .topTrailing) {
NavigationStack(path: $path) {
Color(UIColor.systemBackground)
.ignoresSafeArea()
.navigationBarHidden(true)
.navigationDestination(for: HomeDestination.self) { dest in
destinationView(for: dest)
}
}
FloatingHomeButtons { destination in
path.append(destination)
}
.padding(16)
}
}
@ViewBuilder
private func destinationView(for dest: HomeDestination) -> some View {
switch dest {
case .rnHome:
RNScreen(initialModule: "Home")
case .nativeSettings:
SettingsScreen()
case .rnContact:
RNScreen(initialModule: "Contact")
}
}
}
private struct FloatingHomeButtons: View {
let onSelect: (HomeDestination) -> Void
var body: some View {
VStack(alignment: .trailing, spacing: 12) {
Button("Open RN-Home") { onSelect(.rnHome) }
.buttonStyle(.borderedProminent)
Button("Open Native-Settings") { onSelect(.nativeSettings) }
.buttonStyle(.borderedProminent)
Button("Open RN-Contact") { onSelect(.rnContact) }
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing)
.allowsHitTesting(true)
}
}
struct RNScreen: View {
let initialModule: String
var body: some View {
ReactNativeView(
moduleName: "RNApp",
initialProperties: [
"nativeOsVersionLabel":
"\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)",
"initialModule": initialModule
]
)
.ignoresSafeArea()
.navigationBarHidden(true)
}
}
@available(iOS 16.0, *)
#Preview {
ContentView()
}
```
```tsx
export default function App({
nativeOsVersionLabel,
initialModule = 'Home',
}: AppProps) {
const initialRouteName: InitialModule =
initialModule === 'Contact' ? 'Contact' : 'Home';
return (
);
}
```
## Reproducible Demo
https://github.com/user-attachments/assets/988917ba-31a1-45a1-8e16-d5513f3f593b
Contributor guide
Research direction
Start by reproducing the linked demo and tracing the back-button path that calls popToNative alongside the SwiftUI NavigationStack and RNScreen destinations shown in the issue. Done means popToNative closes only the React Native screens at the top of the stack while preserving the native screen and lower navigation state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, swift, typescript
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100