React Native Tab nested into Stack navigation v5 - best practice to update header - react-native

I am trying to understand what's the best approach to nested React Native Navigation v5.
I have a TabNavigation nested into a Stack.Navigator as follow:
const MainNavigation = () => {
return (
<SafeAreaProvider>
<NavigationContainer>
<StatusBar barStyle="light-content" />
<Stack.Navigator screenOptions={navStackOptions} >
<FirstListStack.Screen name="FirstListStack" component={TabNavigation} options={FirstNavOpt} />
<FirstListStack.Screen name="AnotherView" component={AnotherView} options={AnotherViewNavOpt} />
<SecondListStack.Screen name="SecondListStack" component={TabNavigation} options={SecondNavOpt} />
<ThirdListStack.Screen name="ThirdListStack" component={TabNavigation} options={ThirdNavOpt} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
function TabNavigation() {
return (
<Tab.Navigator
initialRouteName="TabOne"
>
<Tab.Screen name="TabOne" component={TabOne} options={navTabOptions} />
<Tab.Screen name="TabTwo" component={TabTwo} options={navTabOptions} />
<Tab.Screen name="TabThree" component={TabThree} options={navTabOptions} />
</Tab.Navigator>
);
}
};
export default MainNavigation;
Now, when switching between the tabs, the stack navigation header does not get updated.
What's the best approach to access the state of the Stack Navigation and update its state? In particular to update the header buttons?
Let me know if my question is unclear.
Many thanks.

I found a solution, but I am not sure is a good practice.
I changed the logic of the Navigation moving the Tab Nav as parent and the Stack Nav as child.
const Tab = createBottomTabNavigator();
const FirstListStack = createStackNavigator();
const SecondListStack = createStackNavigator();
const ThirdListStack = createStackNavigator();
const MainNavigation = () => {
const navTabOptions = ({ route }) => ({
tabBarVisible: isTabBarVisible(route)
});
return (
<SafeAreaProvider>
<NavigationContainer>
<StatusBar barStyle="light-content" />
<Tab.Navigator>
<Tab.Screen name="First" component={FirstListStackScreen} options={navTabOptions} />
<Tab.Screen name="Second" component={SecondListStackScreen} options={navTabOptions} />
<Tab.Screen name="Third" component={ThirdListStackScreen} options={navTabOptions} />
</Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
function isTabBarVisible(route) {
// Check here if the route.state is not undefined
// and based on the route return true or false
// to show or hide the tab bar
}
function FirstListStackScreen() {
return (
<FirstListStack.Navigator>
<FirstListStack.Screen name="First" component={} />
</FirstListStack.Navigator>
);
}
function SecondListStackScreen() {
return (
<SecondListStack.Navigator>
<SecondListStack.Screen name="Second" component={} />
</SecondListStack.Navigator>
);
}
function ThirdListStackScreen() {
return (
<ThirdListStack.Navigator>
<ThirdListStack.Screen name="Third" component={} />
</ThirdListStack.Navigator>
);
}
};
export default MainNavigation;
Please post any better solution to this.
Thanks

Related

React navigation : FragmentManager is already executing transanctions

I was using BottomTabNavigation to create tabs navigation in react native with nested Stack navigation, but when I want to implement the same logic with createMaterialTopTabNavigator, I get this error. here's the code :
const Stack = createNativeStackNavigator()
const TopTab = createMaterialTopTabNavigator()
const HomeStack = () => {
return <Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={options} />
<Stack.Screen name="TodoDetail" component={TodoDetail} />
</Stack.Navigator>
}
export default function App() {
return (
<NavigationContainer>
<TopTab.Navigator>
<TopTab.Screen name='Home' component={HomeStack} />
<TopTab.Screen name='About' component={About} />
</TopTab.Navigator>
</NavigationContainer>
)
}
here is the error in the device :
The problem is not with the definition of TopTab, but the current implementation leads to the issue.
The TopTab navigation must be in a Stack.Screen. forget about the BottomTabNavigation, it uses a different approach to show the screens.
So You must use TopTabNavigation inside of a screen
Just for an example with the current implementation:
const Stack = createNativeStackNavigator()
const TopTab = createMaterialTopTabNavigator()
const topBarStack = () => {
return (
<TopTab.Navigator>
<TopTab.Screen name='first-tab' component={firstTab} />
<TopTab.Screen name='second-tab' component={secondTab} />
</TopTab.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={topBarStack} options={options} />
<Stack.Screen name="TodoDetail" component={TodoDetail} />
</Stack.Navigator>
</NavigationContainer>
)
}

navigation.goBack not working in react native

This is the structure of my project: Tab navigator has two screens DailyTab and WeeklyTab. DailyTab is itself a stack navigator which has two screens Daily and Todos. Daily is the default screen and I am able to navigate from Daily to Todos but the same Todos screen appears when I press the back button on Todos(I expect it to take me to Daily screen)
Main:
const Main = () => {
const Tab = createBottomTabNavigator();
return (
<NavigationContainer independent={true}>
<Tab.Navigator>
<Tab.Screen
name="DailyTab"
component={DailyTab}
/>
<Tab.Screen
name="WeeklyTab"
component={WeeklyTab}
/>
</Tab.Navigator>
</NavigationContainer>
);
};
DailyTab:
const DailyTab = () => {
return (
<NavigationContainer independent={true}>
<Stack.Navigator initialRouteName="Daily">
<Stack.Screen
name="Daily"
component={Daily}
options={{headerShown: false}}
/>
<Stack.Screen
name="Todos"
component={Todos}
options={{headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
const Daily = ({navigation}) => {
return (
<View style={globalStyles.overallBackground}>
<Navbar />
<Calendar navigation={navigation} />
</View>
);
};
Todos:
const Todos = ({route, navigation}) => {
return (
<View style={globalStyles.overallBackground}>
<Navbar />
<Button title="back" onPress={()=>navigation.goBack()} ></Button>
</View>
);
};
Where am I going wrong?
Try out putting --
this.props.navigation.goBack()

Using two Navigators in an app React native

Trying to use bar Navigation and drawer navigator in the same app, and now sure how to make it work.
So currently In the App.js I have a "NavigationContainer", and inside I have a "BarNavigator". whcih works fine, then I wanna add a "DrawerNavigator" inside the "NavigationContainer" then I got an error "Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app"
then i found this link doing exactly what i wanted ( showing at the end of the page ) and apply what he is doing. still got the same error, then I removed the "BarNavigator" which is the working one, and test out if DrawerNavigator got error, and yes. even got error with the DrawerNavigator only. and here is the code.
App.js
const App = () => {
return (
<NavigationContainer>
//<HomeStackNavigator />
<DrawerNavigator />
</NavigationContainer>
);
};
export default App;
DrawerNavigator.js
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={navigation_bar} />
{/* <Drawer.Screen name="Notifications" component={Ivestment} /> */}
</Drawer.Navigator>
</NavigationContainer>
);
};
export default DrawerNavigator;
Navigator.js
const Stack = createStackNavigator();
const screenOptionStyle = {
headerShown: false,
};
const HomeStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Home" component={BottomTabNavigator} />
<Stack.Screen name="Detail" component={Detail} />
</Stack.Navigator>
);
};
export default HomeStackNavigator;
You are doing it wrong.
You should have only 1 "NavigationContainer" and the navigators should be nested, i.e. one inside of another (parent-child not siblings).
It should look something like this:
App.js
const App = () => {
return (
<NavigationContainer>
<DrawerNavigator />
</NavigationContainer>
);
};
export default App;
DrawerNavigator.js
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeTabNavigator} />
<Drawer.Screen name="OtherScreen" component={OtherScreen} />
</Drawer.Navigator>
);
};
export default DrawerNavigator;
TabNavigator.js
const Tab = createTabNavigator();
const screenOptionStyle = {
headerShown: false,
};
const HomeTabNavigator = () => {
return (
<Tab.Navigator screenOptions={screenOptionStyle}>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Detail" component={Detail} />
</Tab.Navigator>
);
};
export default HomeTabNavigator;
Might be easier to look at it bottom-top as well.

How to create custom header options for each screen multi stack navigation react native?

I am looking for a way to set custom header options (styling, icons, enabling/disabling back button, left and right options for the header) for each of the screen in the stacks, while maintaining multi-stack architecture for my app in react native?
This is how my App.js looks right now, and I am willing to change it if need be.
const Stack = createStackNavigator();
const App = () => {
const ref = React.useRef();
const { getInitialState } = useLinking(ref, {
prefixes: ['FoodApp://'],
});
const AuthStack = createStackNavigator();
function AuthStackScreen() {
return (
<AuthStack.Navigator>
<AuthStack.Screen name="LogIn" component={LogIn} />
<AuthStack.Screen name="SignUp" component={SignUp} />
</AuthStack.Navigator>
);
}
const AppStack = createStackNavigator();
//I'd like to set different header options for each of the screen here
function AppStackScreen() {
return (
<AppStack.Navigator>
<AppStack.Screen name="MenuCategoryItems" component={MenuCategoryItems} />
<AppStack.Screen name="Delivery" component={Delivery} />
<AppStack.Screen name="Account" component={Account} />
<AppStack.Screen name="Notification" component={Notification} />
<AppStack.Screen name="Cart" component={Cart} />
</AppStack.Navigator>
);
}
//TODO: pass customized bar components
const Tab = createBottomTabNavigator();
//I'd like to set different header options for each of the screen here
function Tabs(){
return (
<Tab.Navigator tabBar={props => <BottomMenu {...props} />}>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Delivery" component={Delivery} />
<Tab.Screen name="Account" component={Account} />
<Tab.Screen name="Notification" component={Notification} />
<Tab.Screen name="Cart" component={Cart} />
</Tab.Navigator>
);
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Tabs} />
<Stack.Screen name="AppStack" component={AppStackScreen} />
/*Should I place something else here so that I have access to both AppStack and Tabs navigations?*/
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
Not sure if this is what you need but here is how I customize some "borderColor" value for my header component.
You can pass any options you want via the setOptions method of the navigation component. It will be populated in the scene.descriptor.options parameter.
Below an example of how I am using it in my app.
the header options for my screen/navigator.
header: ({scene, previous, navigation}) => {
const {options} = scene.descriptor;
let borderColor = options.borderColor ?? 'yellow';
if (scene.route.name.startsWith('Test')) {
borderColor = 'blue';
}
return <HeaderBar options={options}
title={title}
previous={true}
navigation={navigation}
borderColor={borderColor}
/>;
}
And in any of the screens components I can use
useLayoutEffect(() => {
navigation.setOptions({
borderColor: 'orange'
})
}, [ navigation ])
Now, my <HeaderBar> component will receive a prop borderColor whose value is 'orange'.
export const HeaderBar = ({ options, title, previous, navigation, borderColor = 'yellow' }) => {
[...]
console.log(borderColor); // orange
}
As you can see, my HeaderBar component also receive the full options as a prop, therefore I could skip the borderColor prop and check the options inside the header component itself.
Like so
export const HeaderBar = ({ options, title, previous, navigation }) => {
const { borderColor } = options;
[...]
}
Hope it will help you if not too late.
Well. You could hide header default of react-navigation in your stack.
function AppStackScreen() {
return (
<AppStack.Navigator headerMode={'none'}>
<AppStack.Screen name="MenuCategoryItems" component={MenuCategoryItems} />
<AppStack.Screen name="Delivery" component={Delivery} />
<AppStack.Screen name="Account" component={Account} />
<AppStack.Screen name="Notification" component={Notification} />
<AppStack.Screen name="Cart" component={Cart} />
</AppStack.Navigator>
Then, you could custom header component and import it in each your screen.
Sorry because my EL. hope help U.

React navigation 5 - header is not shown

Trying to update my app to react navigation 5 and been confronting some issues.
First of all, the header does not show up. Snips from the code:
[from App.js]
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} options={{ title:'some title' }}/>
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
and the style of the current screen:
<View style={{flex:1, flexDirection:'column',justifyContent:'space-between'}}>
Here is a screenshot of the app on an Android emulator (and it looks the same on my phone):
As you can see, the header is not shown, the tab navgiation does not right, and so are the buttons (something changed about their background). I did not change anything in the app besides upgrading to react-navigation 5..
Thanks for the help!
Tab navigators do not have header support. You have to wrap your tab navigator inside a stack navigator.
import { createStackNavigator } from "#react-navigation/stack";
// ... other imports
export const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={TabNavigator} />
</Stack.Navigator>
);
}
const Tab = createTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
);
}