I am trying to configure react-navigation for a web app with react native. For that I set up the linking options on a NavigationContainer so that I can access my pages from a browser url, using this code :
const linking = {
prefixes: ['http://localhost:8080/', 'http://localhost:8080', 'localhost:8080'],
// prefixes: [prefix],
config: {
screens: {
SignIn: "SignIn",
SignUp: "SignUp",
Landing: '*',
},
}
};
function AppContainer() {
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<AppStack.Navigator>
<AppStack.Screen name="SignIn" component={SignInPage}/>
<AppStack.Screen name="Landing" component={LandingPage}/>
<AppStack.Screen name="SignUp" component={SignUpPage}/>
<AppStack.Screen name="Home" component={HomePage}/>
</AppStack.Navigator>
</NavigationContainer>
);
}
When I go to "http://localhost:8080/", I am redirected to "http://localhost:8080/SignIn" ( which is fine), and the app is working. The problem is that if I go from my browser to "http://localhost:8080/SignIn" I get "Cannot GET /SignIn", and the app is not working...
I am using these versions :
"#react-navigation/bottom-tabs": "^5.11.1",
"#react-navigation/native": "^5.8.9",
"#react-navigation/stack": "^5.12.5",
Found a solution on How to tell webpack dev server to serve index.html for any route.
I was using webpack-dev-server, which needs some configurations in the webpack.config.js to map all the url to / and serve the index.html .. These are the configurations :
devServer: {
port: 3000,
historyApiFallback: {
index: 'index.html'
}
}
Adding the cli option --history-api-fallback also do the trick.
Related
Given the following setup:
const config = {
screens: {
Discover: 'discover',
Theater: {
screens: {
Watch: 'watch/:name?',
},
},
},
}
export const linking: LinkingOptions<any> = {
prefixes: ['test://'],
config,
}
const Tab = createBottomTabNavigator()
export function TheaterStackScreen({route}: Props) {
return (
<Tab.Navigator screenOptions={{headerShown: false}}>
<Tab.Screen name="Watch" component={WatchScreen} initialParams={{name: route.params.name}} />
</Tab.Navigator>
)
}
const Stack = createNativeStackNavigator()
export function StackScreen() {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Discover" component={DiscoverScreen} />
<Stack.Screen name="Theater" component={TheaterStackScreen} />
</Stack.Navigator>
)
}
...and running:
npx uri-scheme open "test://watch/test" --android
I recieve:
Android: Opening URI "test://watch/test" in emulator
...with no errors, however, there is no navigation within the app...
and this warning:
WARN The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.
Does something look inherently wrong with this setup?
current setup:
"#react-navigation/bottom-tabs": "6.3.1",
"#react-navigation/native": "6.0.10",
"#react-navigation/native-stack": "6.6.2",
"react-native": "0.68.2",
This is being run on device, but I don't get any different behavior on sim either. Ive attempted with debugger on and off with no difference.
As always any and all direction is appreciated so thanks in advance!
Edit with more details:
const {createNavigationContainer} = Bugsnag.getPlugin('reactNavigation') as BugsnagPluginReactNavigationResult
const BugsnagNavigationContainer = createNavigationContainer(NavigationContainer)
const Stack = createNativeStackNavigator()
...
<BugsnagNavigationContainer linking {linking} onStateChange={(state: any) => {
const newRouteNam = Analytics.getActiveRouteName(state)
if (routeName !== newRouteName) {
analyticsClient.screen(newRouteName)
setRouteName(newRouteName)
}
}}>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name="Main" component={StackScreen} />
</Stack.Navigator>
</BugsnagNavigationContainer>
Edit 2:
Unfortunately, updating the config had no effect, I still see the tracking but no navigation or opening of the specified path:
const config = {
screens: {
Main: {
screens: {
Discover: 'discover',
Theater: {
screens: {
Watch: 'watch/:name',
},
},
},
},
},
}
results:
TRACK (Deep Link Opened) event saved {"event": "Deep Link Opened", "properties": {"referring_application": "android-app://com.android.shell", "url": "test://watch"}, "type": "track"}
Version:
"dependencies": {
"react-native": "0.63.4",
"#react-navigation/bottom-tabs": "^5.11.2",
"#react-navigation/native": "^5.8.10",
"#react-navigation/stack": "^5.12.8",
}
Test website link test://info_register?token=1111 successfully, I can see route.params includes token
but when I get into my Tabs screen, and try to to use test://setting_register?token=1111, App just open it doesn't navigate to SettingScreen and route.params is undefined
I take reference from official document https://reactnavigation.org/docs/5.x/configuring-links
What is wrong with my deep linking for Tabs ?
Here is my code:
index.js
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import LoginStack from './LoginStack';
import Linking from './Linking';
const AppContainer = () => {
return (
<NavigationContainer linking={Linking}>
<LoginStack />
</NavigationContainer>
);
};
export default AppContainer;
Linking.js
const config = {
screens: {
// set config for App init screen
PersonalInfoScreen: {
path: 'info_register/',
parse: {
token: (token) => `${token}`,
},
},
// set config for Tabs screen
Setting: {
screens: {
SettingScreen: 'setting_register/:token',
},
},
},
},
};
const Linking = {
prefixes: ['test://'],
config,
};
export default Linking;
LoginStack.js
import * as React from 'react';
import {useSelector} from 'react-redux';
import {createStackNavigator} from '#react-navigation/stack';
import LoginScreen from '../screens/Login/LoginScreen';
import PersonalInfoScreen from '../screens/Login/PersonalInfoScreen';
import TabStack from './TabStack';
const Stack = createStackNavigator();
const LoginStack = () => {
const {uid, userToken} = useSelector((state) => state.LoginRedux);
const showLoginFlow = uid === '' || userToken === '' ? true : false;
return (
<Stack.Navigator
initialRouteName={'LoginScreen'}
screenOptions={{headerShown: false, gestureEnabled: false}}>
{showLoginFlow ? (
<>
<Stack.Screen name="LoginScreen" component={LoginScreen} />
<Stack.Screen
name="PersonalInfoScreen"
component={PersonalInfoScreen}
/>
</>
) : (
<>
<Stack.Screen name="TabStack" component={TabStack} />
</>
)}}
</Stack.Navigator>
);
};
export default LoginStack;
TabStack.js
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const TabStack = () => {
return (
<Tab.Navigator
screenOptions={...mySetting}
tabBarOptions={...myStyle},
}}>
<Tab.Screen name={'Free'} component={FreeStack} />
<Tab.Screen name={'Video'} component={VideoStack} />
<Tab.Screen name={'News'} component={NewsStack} />
<Tab.Screen name={'Consultation'} component={ConsulationStack} />
<Tab.Screen name={'Setting'} component={SettingStack} />
</Tab.Navigator>
);
};
export default TabStack;
If review nested navigation https://reactnavigation.org/docs/5.x/configuring-links/#handling-nested-navigators docs.
You have this navigation tree for SettingScreen:
TabStack -> Setting -> SettingStack -> SettingScreen.
Routes configuration should match this tree also as below:
const config = {
screens: {
// set config for App init screen
PersonalInfoScreen: {
path: "info_register/",
parse: {
token: (token) => `${token}`,
},
},
// set config for Tabs screen
TabStack: {
screens: {
Setting: {
screens: {
SettingScreen: "setting_register/:token",
},
},
},
},
},
};
Just a note, it seems like whenever you are enabling debug-chrome configuration deep linking with react-navigation is not working, I disabled it and it worked!
I'm trying to enable deeplink in my React Native project, and have it working for all use-cases where the deeplink opens the app from the background, not from the first load. I believe the issue is that my path that I am deep-linking, is not available until after the "loading screen" has gone away.
For example I have this setup as my RootStack.Navigator
<RootStack.Navigator
detachInactiveScreens={false}
headerMode="none"
initialRouteName={"AppTabsScreen"}
screenOptions={{ animationEnabled: false }}
mode="modal"
>
{isLoading ? (
<RootStack.Screen
name="LoadingScreen"
component={LoadingScreen}
options={{ animationEnabled: true }}
/>
) : user ? (
<RootStack.Screen name="AppTabsScreen" component={AppTabsScreen} />
) : (
<RootStack.Screen name="AuthStackScreen" component={AuthStackScreen} />
)}
...
And my linking config as:
const deepLinksConf = {
screens: {
AppTabsScreen: {
screens: {
initialRouteName: "Activity",
Activity: {
screens: {
Activity: "activity",
Details: "workout/:userId/:id",
},
},
Goals: {
screens: {
Goals: "goal",
GoalDetail: "goal/:id",
},
},
Settings: "settings",
Profile: "profile",
},
},
}
};
In my getInitialURL() function, I get the URL correctly, but I believe the path is already on the LoadingScreen, and not the AppTabsScreen, which causes it not to fire. Is there anyway around this situation? How can I delay the deeplink to wait until the AppTabScreen is present to process the link? This works fine when the app is the fore or background, but never at first start.
....
async getInitialURL() {
console.log("fired");
// Check if app was opened from a deep link
const url = await Linking.getInitialURL();
console.log("deeplinking");
console.log(url);
if (url != null) {
console.log("just returning");
return url;
}
// Check if there is an initial firebase notification
const message = await messaging().getInitialNotification();
// Get deep link from data
// if this is undefined, the app will open the default/home page
return messsage.data?.link;
},
I am using react-navigation, and my main navigation controller is as follows:
<NavigationContainer
linking={linking}
theme={scheme === "dark" ? DarkTheme : DefaultTheme}
ref={navigationRef}
onReady={() => {
if (navigationRef.current) {
routeNameRef.current = navigationRef?.current.getCurrentRoute().name;
}
}}
onStateChange={async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef?.current.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
await analytics().logScreenView({
screen_name: currentRouteName,
screen_class: currentRouteName,
});
}
// Save the current route name for later comparision
routeNameRef.current = currentRouteName;
}}
>
<FlashMessage position="top" />
<RootStackScreen />
</NavigationContainer>
I'm having some trouble migrating my deep-link from React Navigation v4 to React Navigation v5. 😊
For context, my deep linking has been working perfectly in React Navigation v5 with a tab bar navigator and stack navigators in each tab.
Here's what that looked like:
const MainApp = createBottomTabNavigator(
{
DiscoverTabStack: { screen: DiscoverTabStack, path: "" },
GroupTabStack: { screen: GroupTabStack, path: "" },
ProfileTabStack: { screen: ProfileTabStack, path: "" },
},
);
const DiscoverTabStack = createStackNavigator(
{
Discover: { screen: DiscoverScreen, path: "discover" },
DetailedActivityFromDeepLink: {
screen: DetailedActivityFromDeepLinkScreen,
path: "discover/activites/:id",
},
}
With React Navigation v4, I'm able to successfully deep link into the app to the correct place. However, I'm having some trouble with React Navigation v5. Below is how I'm approaching it with Reach Navigation v5.
const Tab = createBottomTabNavigator();
const DiscoverStack = createStackNavigator();
const prefixes = Linking.makeUrl("myapp://");
const linking = {
prefixes: [prefixes],
config: {
screens: {
DiscoverStack: {
path: "",
screens: {
Discover: {
path: "discover",
},
DetailedActivityFromDeepLink: {
path: "discover/activites/:id",
parse: {
id: (id) => `${id}`,
},
},
},
},
},
},
};
const DiscoverScreens = ({ navigation, route }) => {
return (
<DiscoverStack.Navigator mode="card">
<DiscoverStack.Screen
name="Discover"
component={DiscoverScreen}
/>
<DiscoverStack.Screen
name="DetailedActivityFromDeepLink"
component={DetailedActivityFromDeepLinkScreen}
/>
</DiscoverStack.Navigator>
);
};
render() {
return (
<Container>
<NavigationContainer linking={linking}>
<Tab.Navigator>
<Tab.Screen
name="Discover"
component={DiscoverScreens}
/>
</Tab.Navigator>
</NavigationContainer>
</Container>
);
}
Unfortunately, the above is not working. What may be wrong with my approach? How does one deep-link into an app that has tab bars with stack navigators in each tab?
I'm sure that this is a challenge for the majority of apps out there, so it'll be awesome for some help!! Thanks in advance!
What may be wrong with my approach?
In your root navigator (Tab.Navigator), you call your screen Discover, but in the linking config, you have written DiscoverStack.
The linking config needs to have the same names as you have your screens.
I am trying to implement a deep link on Android ( Similar to iOS universal link). Please find below the SDK details.
SDK Version: 37.0.12
Platforms(Android/iOS/web/all):
Android
app.json contains an intent filter as suggested
"intentFilters": [
{
"action": "VIEW",
"data": [
{
"scheme": "https",
"host": "**************",
"pathPrefix": "/records"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
below is app.js code.
const Stack = createStackNavigator()
function App() {
const linking = { prefixes: ['*******','exps://********'] }
return (
<NavigationContainer linking={linking} fallback={<Text>Loading...</Text>}>
<Stack.Navigator>
<Stack.Screen name='Home' component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}
export default App
and the server contains asset links hosted on well-known/assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "************",
"sha256_cert_fingerprints": [ "5C:AF:56:11:A5:3C:82:A7:9D:18:23:6E:CC:67:08:16**********"
]
}
}
]
The app runs as expected however, Deep link doesn’t seem to work.
am I missing something? Any leads/suggestion/input will be highly appreciated
Regards,
H