react-navigation/stack: header not showing in sub stack after clean build - react-native

Since doing a clean build of our app the headers of sub stacks don't show anymore.
I assume the removal and recreation of the yarn.lock file updated some dependencies correctly, so we are no longer lingering on some old versions.
I tried another explicit update of react-navigation/native and react-navigation/stack to the latest (currently 5.7.3 and 5.9.0), but to no avail.
Our navigation setup is one root stack with multiple sub stacks below that. The root stack is the main app navigation, like login and dashboard. The sub stacks are for blocks of functionality available via the dashboard, one each per block.
The sub stack headers can vary in style and content.
I tried adjusting the Stack.Navigator headerMode or screenOptions and the Stack.Screen options, but I couldn't find a solution. So any help is appreciated.
A simplified version of the code showing the issue is this:
// Root navigation
enum RootRoutes {
Login = 'Login',
Dashboard = 'Dashboard',
Sub = 'Sub',
}
type RootStackParamList = {
[RootRoutes.Login]: undefined;
[RootRoutes.Dashboard]: undefined;
[RootRoutes.Sub]: undefined;
};
const RootStack = createStackNavigator<RootStackParamList>();
const RootStackNavigation = () => {
return (
<RootStack.Navigator
headerMode='screen'
initialRouteName={RootRoutes.Login}
screenOptions={{
headerStyle: {
elevation: 0,
},
}}
>
<RootStack.Screen
component={LoginScreen}
name={RootRoutes.Login}
/>
<RootStack.Screen
component={DashboardScreen}
name={RootRoutes.Dashboard}
options={{
title: 'Dashboard',
}}
/>
<RootStack.Screen
component={SubStackNavigation}
name={RootRoutes.Sub}
options={{
header: () => null, // needed to hide the header on sub screens
}}
/>
</RootStack.Navigator>
);
};
// Sub navigation
enum SubRoutes {
Index = 'SubIndex',
}
type SubStackParamList = {
[SubRoutes.Index]: undefined;
};
const SubStack = createStackNavigator<SubStackParamList>();
const SubStackNavigation = () => {
return (
<SubStack.Navigator
headerMode='screen'
initialRouteName={SubRoutes.Index}
screenOptions={{
headerTransparent: true,
}}
>
<SubStack.Screen
component={IndexScreen}
name={SubRoutes.Index}
options={{
title: 'Sub screen',
}}
/>
</SubStack.Navigator>
);
};
// App.tsx
export const App = () => {
return (
<NavigationContainer><RootStackNavigation/></NavigationContainer>
);
};
Only removing header: () => null made a difference, but this shows the root header, with incorrect styling and not the custom content (apart from title) and also navigating in the root stack alone.

Ok, this turned out to be a small difference in the latest react navigation version. Instead of hiding the root header for the sub stack with
options={{
header: () => null,
}}
in its Stack.Screen, it needs to be with
options={{
headerShown: false,
}}

Related

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

how to pass params to nested navigators using React Navigation

Can't pass params to the nested navigator. I keep getting errors undefined is not an object evaluating route.params.data
I do have different stack navigators for homeDrawer and LiveModeDrawer. I am using React navigation version 6. I looked over the documents however what I have read is not working.
app.js
<Drawer.Navigator>
<Drawer.Screen
name="HomeDrawer"
options={{ headerShown: false }}
component={MainNavigator}
/>
<Drawer.Screen
name="LiveModeDrawer"
options={{ headerShown: false }}
component={LiveModeStackNavigator}
/>
</Drawer.Navigator>;
todo.js
this.props.navigation.navigate("LiveModeDrawer", {
screen: "LiveModeAlertOnly",
params: { data: "jane" },
});
live.js
export default function LiveModeAlertsNotifications({ route, navigation }) {
const { data } = route.params;
console.info(data);
}
AFAIK the second parameter already are the Params
Change this:
this.props.navigation.navigate('LiveModeDrawer', {
screen: 'LiveModeAlertOnly',
params: {data: 'jane'},
});
To:
this.props.navigation.navigate('LiveModeDrawer', {
data: 'jane,
});
Or:
export default function LiveModeAlertsNotifications({route, navigation}) {
const {data} = route.params.params; // Add another params
console.info(data)
I forgot to pass the prop into a sub component

React Navigation 5: Switching between different stack navigators in React native

I'm finding difficulty in digesting the official documentation and I'm stuck finding out a solution to move between different stack navigators. I have provided my current implementation and code snippet to explain my problem better.
I have a bottom tab navigator and 2 stack navigators to handle different use cases.
BottomNavigation.js
StackNavigation.js
AuthStackNavigation.js
I have created multiple stack navigators within StackNavigation.js and rendering each StackNavigators within BottomNavigation.js
***StackNavigation.js***
const Stack = createStackNavigator();
const HomeStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="Home" screenOptions={ScreenLogo}>
<Stack.Screen name="HomeScreen" component={Home} />
</Stack.Navigator>
);
}
const ProfileStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="Home" screenOptions={ScreenLogo}>
<Stack.Screen name="MahaExpo" component={Profile} />
</Stack.Navigator>
);
}
export { HomeStackNavigator, ProfileStackNavigator };
And as I said I'm rendering each navigator inside tab navigator to switch between screens.
***BottomNavigation.js***
import { HomeStackNavigator, ProfileStackNavigator } from '../Navigations/StackNavigation'
const Tab = createMaterialBottomTabNavigator();
function BottomNavigation() {
return (
<Tab.Navigator
initialRouteName="Home" >
<Tab.Screen
name="Home"
component={HomeStackNavigator}
/>
<Tab.Screen
name="ProfileStackNavigator"
component={ProfileStackNavigator}
/>
</Tab.Navigator>
)
}
export default BottomNavigation
and I'm rendering this within app.js and inside NavigationContainer. I have created AuthStackNavigation.js which has a login and register screens.
***AuthStackNavigation.js***
const AuthStack = createStackNavigator();
const AuthStackNavigation = ({ }) => (
<AuthStack.Navigator headerMode='none'>
<AuthStack.Screen name="UserLogin" component={UserLogin}></AuthStack.Screen>
<AuthStack.Screen name="UserRegister" component={UserRegister}></AuthStack.Screen>
</AuthStack.Navigator>
);
export default AuthStackNavigation
So currently, I'm showing the home screen with some public content and user can switch to different screens using the bottom tab navigator linked to different screens. When the user clicks on profile tab, im displaying a button to login which should take the user to AuthStackNavigation.js which has a login and register screen. Thanks in advance!
const ProfileStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="Home" screenOptions={ScreenLogo}>
<Stack.Screen name="MahaExpo" component={Profile} />
<Stack.Screen name="UserLogin" component={UserLogin} />
<Stack.Screen name="UserRegister" component={UserRegister} />
</Stack.Navigator>
);
}
on profile page check user is logged in or not, if not, simply navigate to UserLogin screen. Once login simply pop back to profile page are refresh.
There's no way of communicating between two separate navigators if they're not related to each other (one is a child navigator of another or etc.), unless you create something like RootStackNavigator, which would be on the higher level and which would contain all the navigators you have listed above.
Create guest navigators array, authorized navigators array and some shared routes (if needed). Guest navigator contains authstack only and authorized one contains other navigators, if you use redux, context or something like this you can check for the token on startup to determine which navigator you have to use. Logging in will give you the token and will automatically change your navigator to authorized navigators and logging out will throw you back (as you no longer have the token) to the guest navigators which will contain only authentication flow.
Pretty hard to explain ;) hope this helps..
Redux Example
`
const authNavigators = [
{
type: "screen",
key: "authNavigators",
name: "authNavigators",
component: authNavigators,
},
];
const otherNavigators = [
{
type: "screen",
key: "otherNavigators1",
name: "otherNavigator1",
component: otherNavigators1,
},
{
type: "screen",
key: "otherNavigators2",
name: "otherNavigator2",
component: otherNavigators3,
},
...
];
const RootStack = () => {
const { condition } = useSelector(
(state) => state.store);
let navigators = otherNavigators;
if(condition) {
navigators = authNavigators;
}
return (
<RootStackNavigator.Navigator>
{navigators.map((item) => {
return (
<RootStackNavigator.Screen
{...item}
options={({ route: { params } }) => ({
...item.options,
})}
/>
);
})}
</RootStackNavigator.Navigator>
);
};
export default RootStack;`
from redux, you can dispatch an action which would change that condition and this would dynamically update your navigator.

React-Navigation useIsFocused always returns true, and I can't figure out why?

I have pretty simple use case for this, Basically if the screen is focused I want this function to run, if not don't run.
const isFocused = useIsFocused();
const onViewRef = useRef(({ viewableItems }) => {
if (!isFocused) return;
//code to run
});
Here's the stack for this screen in particular:
const MyStack = () => {
return (
<Stack.Navigator headerMode="none">
<Stack.Screen
component={MyStackComponent}
name="My"
/>
</Stack.Navigator>
);
};
And here is that stack being use in a tabNavigator
<Tab.Screen
component={MyStack}
name="My"
/>
<Tab.Screen
component={Other Stack}
name="Other"
/>
So basically whenever I go to any other tab on my app, the file using the useIsFocused hook should should return false right? Because right now it returns true no matter what, and I can't have that function running all the time. Any pointers?
to bring a real answer to this topic.
For some reason, in my case too, useIsFocused() always return true; from the moment I arrive on the screen in question for the first time untill I kill the app (IOS).
So I found another solution: use the navigation.isFocused() method from react-navigation. (https://reactnavigation.org/docs/navigation-prop/#isfocused)
And it works perfectly !
Hoping it will help someone
Here is my example:
useEffect(() => {
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
if (navigation.isFocused()) {
fetchConversationsList();
}
});
}, [fetchConversationsList, navigation]);
https://reactnavigation.org/docs/tab-based-navigation
tabBarIcon: ({ focused, color, size })

How to hide header of createStackNavigator on React Native?

I want to hide header because I already have styled Toolbar in code:
import {createStackNavigator}
from 'react-navigation'
const AppStackNavigator = createStackNavigator ({
Home: HomePage,
Friend: AddFriend,
Bill: AddBill,
})
class App extends Component {
render() {
return (
<AppStackNavigator initialRouteName='Home'/>`<br>
);
}
}
export default App;
What should I add to my code?
update your code like this code
const AppStackNavigator = createStackNavigator ({
Home: {
screen: HomePage,
navigationOptions: {
header: null,
},
},
})
and if you dont want the header for all screens, then
const AppStackNavigator = createStackNavigator ({
Home: {
screen: HomePage,
},
},
{
navigationOptions: {
header: null,
},
})
Note: This solution is for an old version of React Navigation.
To disable headers for all views in a createStackNavigator, you can use headerMode option.
const AppStackNavigator = createStackNavigator({
Home: HomePage,
Friend: AddFriend,
Bill: AddBill,
},
{
headerMode: 'none',
})
Reference: StackNavigatorConfig - createStackNavigator - React Navigation
Can you try:
static navigationOptions = {
header: null
}
Inside your screen component.
For hiding headers for specific screens or globally, you can do
const StackNavigator = createStackNavigator({
Home: {
screen: HomePage,
navigationOptions: {
header: null // Will hide header for HomePage
}
}
}, {
navigationOptions: {
header: null // Will hide header for all screens of current stack navigator,
headerLeft: <HeaderLeft /> // Component to be displayed in left side of header (Generally it can be Hamburger)
headerRight: <HeaderRight /> // Component to be displayed in right side of header
}
})
Also note that, screen specific settings will override global settings.
Hope, this helps.
try this
options ={{ header: () => {}}}
since you are explicitly not providing any argument to header function, it won't show any header.
For more information refer this: react native docs
I used following code to hide the header.
{
navigationOptions: {
header: null // Will hide header for all screens of current stack
}
2020 UPDATE - REACT NAVIGATION 5+
Using header : null will not work anymore. In the options you need to set both headerMode to none along with headerShown to false as below:
<AuthStack.Navigator>
<AuthStack.Screen name="AUTH" component={AuthScreen} options={{headerMode: 'none', headerShown : false}}/>
</AuthStack.Navigator>
All the answers I could find were from React navigation v4 for some reason, which doesn't work in v5. After spending some time on it I figured out a way to hide toolbar in v5. Here it is:
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
...
const Stack = createStackNavigator();
....
//and inside render
render(){
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: "Home",
headerShown: false,
}}
/>
}
headerShown: false, this will do the trick
If you need help with migrating from v4 to v5 ->
https://reactnavigation.org/docs/upgrading-from-4.x/
navigationOptions: {
header: null
}
is deprecated. The new is
navigationOptions: {
headerShown: false
}
Reference: https://stackoverflow.com/a/62732551/8724367
React Navigation 6 (from October'21)
The solution below is not working anymore:
navigationOptions: {
header: null,
},
Instead of navigationOptions, you want to use just options, and with headerShown set to true, which replaces the header set to null. The working solution:
options={{
headerShown: true,
}}