Not getting route.params when using deep linking - react-native

I'm trying to implement Deep Linking on my APP, I'm following expo-cli react-native-navigation documentation about this subject. After basic configuration I can't get params from route.params from any of the links I set.
This is an example of my code
linking.js
import * as Linking from "expo-linking";
const prefix = Linking.createURL("/");
const config = {
screens: {
userStartSession: {
path: "home/:itemInfo",
parse: { itemInfo: (itemInfo) => `${itemInfo}` },
},
},
};
const linking = {
prefixes: [prefix],
config,
};
export default linking;
Docs here: https://reactnavigation.org/docs/deep-linking/
And here: https://reactnavigation.org/docs/configuring-links/
Then import linking into Navigation.js
import React from "react";
import linking from "../utils/linking/linking";
export default function Navigation() {
return (
<NavigationContainer linking={linking} fallback={<Text>Cargando...</Text>}>
<Stack.Navigator>
<Stack.Screen
name="userStartSession"
options={{ headerShown: false, headerLeft: null }}
>
{(props) => (
<UserSessionStack
{...props}
somePropsHere={somePropsHere}
/>
)}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
);
}
UserSessionStack.js
import React, { useState, useEffect } from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { useRoute } from "#react-navigation/native";
const Stack = createStackNavigator();
export default function UserSessionStack(props) {
const { somePropsHere } = props;
const route = useRoute();
console.log(route.params);
return (
<Stack.Navigator
initialRouteName="slide-home"
>
<Stack.Screen
name="slide-home"
component={SlideHome}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
}
Then following react-navigation-native I run this comand to test the link
npx uri-scheme open exp://192.168.1.101:19000/--/home/this_is_a_test --ios
Docs here:
https://reactnavigation.org/docs/deep-linking/#test-deep-linking-on-ios
As you may see I'm passing "this_is_a_test" as a param to home link, but if I do console.log(route) in userStartSession component, I always get undefined in params
This is the response
Object {
"key": "userStartSession-CLSUaGx7QkCe8qlrvuvTf",
"name": "userStartSession",
"params": undefined,
"state": Object {
"index": 0,
"key": "stack-cyZDx2Q4gdGaZ1dMr2V1Q",
"routeNames": Array [
"slide-home",
"walkthrough-slides",
"sign-up-invitation",
"login-form",
"recover-account-password",
"sign-up-form",
"sign-up-payment-form",
"sign-up-payment-success",
],
"routes": Array [
Object {
"key": "sign-up-invitation-SSf39_Tj79VFv34ydou3N",
"name": "sign-up-invitation",
"params": undefined,
},
],
"stale": false,
"type": "stack",
},
}

Related

How to test bottom tab bar using react native test library

I am trying to do testing for my react bottom tab bar component while testing it I am getting below error.
I followed all the solutions available in this Link no luck for me.
https://github.com/react-navigation/react-navigation/issues/8669
/Users/apple/Documents/MM/myproject/node_modules/#react-navigation/elements/lib/commonjs/assets/back-icon.png:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){�PNG
My testing component
Tabbar.test.js
import React from "react";
import { render, fireEvent } from "#testing-library/react-native";
import Tabbar from "../Tabbar";
it("Tab tests", () => {
const addItemButton = render(<Tabbar />).toJSON;
}
My Tab bar component
Tab.js file
import React from "react";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { Image } from "react-native";
const Tab = createBottomTabNavigator();
const Tabbar = ({ tabData }) => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
tabBarActiveTintColor: "00000",
tabBarInactiveTintColor: "FFFF",
})}
>
{tabsInfo.map((element) => {
return (
<Tab.Screen
key={element.idx}
name={element.tabName}
component={element.component}
/>
);
})}
</Tab.Navigator>
);
};
export default Tabbar;
This is my jest config code
jest.config.js
module.exports = {
preset: "react-native",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
setupFilesAfterEnv: ["#testing-library/jest-native/extend-expect"],
transformIgnorePatterns: [
"node_modules/(?!(#react-native|react-native|react-native-vector-icons)/)",
],
};
Update transformIgnorePatterns in the jest config file.
transformIgnorePatterns: [
"node_modules/(?!(jest-)?react-native|react-clone-referenced-element|#react-native-community|rollbar-react-native|#fortawesome|#react-native|#react-navigation)",
],
Reference link https://github.com/react-navigation/react-navigation/issues/8669

Unable to get to another screen from react-navigation deep linking

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"}

react navigation deep linking not working when use Tabs Stack

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!

Show Onboarding screen once

Does anyone know where I have gone wrong here?
I am trying to dispatch an action when the user taps a button that calls the onSkip function, In the reducer I set the item to local storage and I retrieve it in the rootstack component so I can conditionally set the initial screen in my stack navigator. It always returns false and goes to the onboarding screen instead of login screen.
import React, { useEffect } from 'react'
import { NavigationContainer } from '#react-navigation/native'
import { createNativeStackNavigator } from '#react-navigation/native-stack'
import { RootStackParamList } from '../interfaces/RootStackParamList'
import AsyncStorage from '#react-native-async-storage/async-storage'
import { theme } from '../themes/themes'
import { useAppSelector } from '../app/hooks'
import DrawerNav from './DrawerNav'
import Tabs from './Tabs'
import HomeScreen from '../screens/HomeScreen'
import Login from '../screens/Auth/Login'
import OnboardingScreen from '../screens/Onboarding/OnboardingScreen'
import OtpScreen from '../screens/Auth/OtpScreen'
function RootStack() {
const Stack = createNativeStackNavigator<RootStackParamList>()
const onboardingState = useAppSelector(
(state) => state.onboarding.viewedOnBoarding
)
const [onboarded, setOnboarded] = React.useState(false)
const checkOnboarding = async () => {
try {
const value = await AsyncStorage.getItem('#onBoarding')
if (value !== null) {
setOnboarded(true)
}
} catch (err) {
} finally {
}
}
useEffect(() => {
checkOnboarding()
}, [])
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={
onboarded === true ? 'Login' : 'OnboardingScreen'
}
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen options={{}} name='Login' component={Login} />
<Stack.Screen
name='OnboardingScreen'
component={OnboardingScreen}
/>
<Stack.Screen name='Tabs' component={Tabs} />
<Stack.Screen name='DrawerNav' component={DrawerNav} />
<Stack.Screen
name='OtpScreen'
component={OtpScreen}
options={{
title: 'OTP',
headerShown: true,
headerStyle: {
backgroundColor: theme.colors.whiteSmoke,
},
}}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default RootStack
import { createSlice, PayloadAction } from '#reduxjs/toolkit'
import AsyncStorage from '#react-native-async-storage/async-storage'
export interface onboardingState {
viewedOnBoarding: boolean
}
const initialState: onboardingState = {
viewedOnBoarding: false,
}
export const onboardingSlice = createSlice({
name: 'onboarding',
initialState,
reducers: {
setOnboardingAsync: (state, action: PayloadAction<boolean>) => {
state.viewedOnBoarding = action.payload
AsyncStorage.setItem('#onBoarding', JSON.stringify(action.payload))
},
},
})
// Action creators are generated for each case reducer function
export const { setOnboardingAsync } = onboardingSlice.actions
export default onboardingSlice.reducer
onSkip={() => (
dispatch(setOnboardingAsync(true)),
navigation.replace('Login'),
AsyncStorage.setItem('#onBoarding', JSON.stringify(true))
)}
If you want to persist user's onboarding status, just use Redux-persist in your project (no need to directly use AsyncStorage).
After you implemented redux-persist, just use the
const onboardingState = useAppSelector((state) => state.onboarding.viewedOnBoarding)
to check whether user is onboarded.

How reset tab history in Tabs on tab click using ReactNavigation 5?

I am using React Navigation5.
I have a tab navigator and want to clear history of tab on click tab.
For example, I am on tab 1, and go to tab 2.
From tab2, i navigate
screen1->screen2->screen3
Now If i click on tab, it should come to initial screen (screen1).
but its not working, it working in wrong way.
here is my code.
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { CommonActions, StackActions } from '#react-navigation/native';
// Custom imports:
import StartScreen from '../screens/start/StartScreen';
import ProductsScreen from '../screens/products/ProductsScreen';
import AddCustomerScreen from '../screens/customers/AddCustomerScreen';
import CustomersScreen from '../screens/customers/CustomersScreen';
import WebviewScreen from '../screens/webview/WebviewScreen';
// Menu Screen
import MenuScreen from '../screens/menu/MenuScreen';
import CurrentAccountScreen from '../screens/menu/CurrentAccountScreen';
import AccountDetailScreen from '../screens/menu/AccountDetailScreen';
import AppInfoScreen from '../screens/menu/AppInfoScreen';
import MessagesScreen from '../screens/menu/MessagesScreen';
import { Colors, GlobalStyles } from '../constants';
import { Badge, CustomIcon } from '../components/icons';
import {
iconStart,
iconProducts,
iconCustomers,
iconMenu
} from '../components/icons/TabBarIcons';
import {
IconLowbarAddOrder,
} from '../components/IconFiles';
const Stack = createStackNavigator();
// Initial Parameters for WebView
let initParamsForWebView = {
url: '',
screenName: '',
haveUser: false,
user: false
};
/**
* Start Stack Navigator
**/
const INIT_START_ROUTE = 'Start';
function StartStack() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={INIT_START_ROUTE}>
<Stack.Screen name="Start" component={StartScreen} />
<Stack.Screen name="Webview"
component={WebviewScreen}
initialParams={initParamsForWebView}
/>
</Stack.Navigator>
);
}
/**
* Products Stack Navigator
**/
const INIT_PRODUCTS_ROUTE = 'Products';
function ProductsStack() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={INIT_PRODUCTS_ROUTE}>
<Stack.Screen name="Products" component={ProductsScreen} />
<Stack.Screen name="Webview"
component={WebviewScreen}
initialParams={initParamsForWebView}
/>
</Stack.Navigator>
);
}
/**
* Menu Stack Navigator
**/
const INIT_MENU_ROUTE = 'Menu';
function MenuStack() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={INIT_CUSTOMERS_ROUTE}>
<Stack.Screen name="Menu" component={MenuScreen} />
<Stack.Screen name="CurrentAccount" component={CurrentAccountScreen} />
<Stack.Screen name="AccountDetail" component={AccountDetailScreen} />
<Stack.Screen name="AppInfo" component={AppInfoScreen} />
<Stack.Screen name="Messages" component={MessagesScreen} />
<Stack.Screen name="Webview"
component={WebviewScreen}
initialParams={initParamsForWebView}
/>
</Stack.Navigator>
);
}
function resetStack(navigation, _route, _stack, _screen){
console.log(_route);
console.log(navigation);
console.log('ResetStack Called');
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{ name: _stack}
]
})
);
}
const BottomTab = createBottomTabNavigator();
const INITIAL_ROUTE_NAME = 'StartStack';
export default function ParticipantNavigator({ navigation, route }) {
// screenOptions={{ headerShown: false }}
return (
<BottomTab.Navigator
screenOptions={{ headerShown: false }}
initialRouteName={INITIAL_ROUTE_NAME}
lazy='false'
tabBarOptions={{}}>
<BottomTab.Screen
name="StartStack"
component={StartStack}
options={{
title: 'Start'
}}
listeners={{
tabPress: e => {
resetStack(navigation, route, 'StartStack', INIT_START_ROUTE);
}
}}
/>
<BottomTab.Screen
name="ProductsStack"
component={ProductsStack}
options={{
title: 'Products'
}}
listeners={{
tabPress: e => {
resetStack(navigation, route, 'ProductsStack', INIT_PRODUCTS_ROUTE);
}
}}
/>
<BottomTab.Screen
name="MenuStack"
component={MenuStack}
options={{
title: 'Menu'
}}
listeners={{
tabPress: e => {
resetStack(navigation, route, 'MenuStack', INIT_MENU_ROUTE);
}
}}
/>
</BottomTab.Navigator>
);
}
Two issues in this code i am facing.
When I click on Tab, it goes to first tab instead of moving to first screen of Clicked tab.
When i come back to old tab, History not reset on that tab too.
any one can help me in this, Thanks.
if you are wroking on tabs and then want to reset the tab then try this React Navigation 5 here is the linksee the documentation React Navigation 5
<BottomTab.Screen
name="Post"
component={PostStack}
options={{
unmountOnBlur: true,// set this props in your tab screen options
title: 'Post',
tabBarIcon: focused => <TabBarIcon focused={focused} name="Post" />,
}}
/>
you can try this porp:unmountOnBlur
when you from ProductsStack to MenuStack,the ProductsStack will Unmount,
link is here
Here is the solution with React Navigation 5.x
https://stackoverflow.com/a/66982007/10672805
The code below is for resetting multiple tabs.
TabNavigator
Tab1: 'tab1_name'
StackNavigator
- ScreenA
- ScreenB
Tab2: 'tabs_name'
StackNavigator
- ScreenC
- ScreenD
Tab3: 'tab3_name'
StackNavigator
- ScreenE
- ScreenF
navigation.dispatch(
CommonActions.reset({
routes: [
{
name: 'tab1_name',
state: {
routes: [
{ name: 'ScreenA' },
{ name: 'ScreenB' },
]
}
},
{
name: 'tab2_name',
state: {
routes: [
{ name: 'ScreenC' },
]
}
},
{
name: 'tab3_name',
state: {
routes: [
{ name: 'ScreenE' },
{ name: 'ScreenF' },
]
}
},
]
})
)
And with this code, the first page you see is tab1_name tab's ScreenB screen.
So, if you want to see tab3_name tab's ScreenF screen first after running the dispatch function, the code should be something like:
navigation.dispatch(
CommonActions.reset({
routes: [
{
name: 'tab3_name',
state: {
routes: [
{ name: 'ScreenE' },
{ name: 'ScreenF' },
]
}
},
{
name: 'tab1_name',
state: {
routes: [
{ name: 'ScreenA' },
{ name: 'ScreenB' },
]
}
},
{
name: 'tab2_name',
state: {
routes: [
{ name: 'ScreenC' },
]
}
},
]
})
)
By the way, when you write down the routes of tab's state, it should follow the sequence of page stack. So that it would work as you expected.
navigation.dispatch(
CommonActions.reset({
routes: [
{
name: 'tab3_name',
state: {
routes: [
// { name: 'ScreenE' }, // removed
{ name: 'ScreenF' },
]
}
},
...
If you omit the first stack page as above, ScreenE is not accessible but only ScreenF after running the dispatch function.
Hope this works for you.