I am using create Bottom Tab Navigator , version 6 , is there a way to pass navigation object from Tab.Screen to TabButton ? taking in account props of tabBarButton that I must also pass them to my custom component (TabButton) , here is my code thanks for your help :
<Tab.Screen key={index} name={item.route} component={item.component}
options={{
headerShown: false,
tabBarButton: (props) => <TabButton {...props} item={item} />
}}
/>
you can pass navigation and tabBarButton props in this way,
The options function receives the navigation prop and the route prop for that screen, so you may also use route there.
<Tab.Screen key={index} name={item.route} component={item.component}
options={({ navigation }) => ({
headerShown: false,
tabBarButton: (props) => <TabButton {...props} navigation={navigation} item={item} />
})
}
/>
Here's the example how you can do it
<Stack.Screen
name={"ComponentName"}
component={ComponentScreen}
options={(props) => {
return {
header: () => (
<CustomHeaderComponent
title={"SomeCoolTitle"}
{...props}
/>
),
};
}}
/>
I just added a global variable and affect to it props.navigation , like so :
return (
<Tab.Screen key={index} name={item.route} component={item.component}
options={( props ) => {
_navigation = props.navigation
return{
tabBarButton: (props) => <TabButton {...props} item={item} navigation={_navigation} />
}
}
}
/>
)
Related
Is there a way to show a modal from drawer in React Native?
There was a similar question and I changed my code following the answer.
My current code is like this.
MyRootStackNavigator.tsx
const Stack = createStackNavigator<RootStackParamList>();
const MyRootStackNavigator = () => {
return (
<Stack.Navigator mode="modal">
<Stack.Screen
name="Main"
component={MyDrawerNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
name="MyModal"
component={MyModal}
/>
</Stack.Navigator>
);
};
MyDrawerNavigation.tsx
const Drawer = createDrawerNavigator();
const MyDrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Home"
component={MyStackNavigator}
/>
<Drawer.Screen
name="About this app"
component={About}
/>
</Drawer.Navigator>
);
}
But for this code the section to show a modal doesn't appear on drawer. Only Home and About this app sections appear on drawer. How can I set the section to show a modal on drawer?
You can use a CustomDrawerContent which has a modal inside and use a button to open the Modal
function CustomDrawerContent(props) {
const [modalVisible, setModalVisible] = useState(false);
return (
<DrawerContentScrollView {...props}>
<Modal
style={{ flxe: 1, backgroundColor: 'red' }}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
<Text>Modal Content Here</Text>
<Button title="Close" onPress={() => setModalVisible(false)} />
</Modal>
<DrawerItemList {...props} />
<DrawerItem
label="Open Modal"
onPress={() => {
setModalVisible(true);
}}
/>
</DrawerContentScrollView>
);
}
And the DrawerNavigator should be setup like this
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}>
You can refer the docs here
https://reactnavigation.org/docs/drawer-navigator/#providing-a-custom-drawercontent
If you want to continue with the code that you have put in the question, you can do like below to navigate to the about screen (Instead of having the modal inside the content)
<DrawerItem
label="Open Screen"
onPress={() => {
props.navigation.navigate('About')
}}
/>
I have problem regarding disappeared header title, Currently I used Nested StackNavigator and DrawerNavigator, I have module when the user is logged in they use the drawer navigator else they will back to stack navigator.
Problem: Why does my header title disappeared ?
Here is my App.js
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const UnauthenticatedScreen = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Login"
component={Login}
options=
{{
headerShown: false,
}}
/>
<Stack.Screen
name="Registration"
component={Register}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTintColor:'#fff',
}}
/>
<Stack.Screen
name="Privacy"
component={PrivacyPolicy}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTitle:'Privacy Policy',
headerTintColor:'#fff',
}}
/>
<Stack.Screen
name="RegistrationSuccess"
component={RegistrationSuccess}
options=
{{
headerShown: false,
}}
/>
</Stack.Navigator>
)
}
const AuthenticatedDriverScreen = () => {
return (
<Drawer.Navigator drawerContent={props=><CustomDriverDrawer {...props} />} initialRouteName="DriverDashboard">
<Drawer.Screen
name="DriverDashboard"
component={DriverDashboard}
options={
{
drawerLabel: 'Home',
drawerIcon: ({focused, color, size}) => (
<Icon type="font-awesome" name="home" style={{fontSize:size, color:"black"}} />
),
}
}
/>
</Drawer.Navigator>
)
}
function Navigator() {
const isLogin = true;
return (
<NavigationContainer>
{isLogin ? <AuthenticatedDriverScreen/> : <UnauthenticatedScreen/>}
</NavigationContainer>
)
}
Authenticated User Screen:
Very well appreciated for your help.
First of all, you need to remember that thing React Navigation providing drawer functionality and screen functionality separately.
for connecting two functionality in one you need to bind the screen with a drawer so you can see both things in one go.
that's why first you need to make one screen navigator that contains single/multiple screen(s) so you can bind that navigator with a drawer like the below code.
const BindedScreenComponent = () => {
return (
<Bind.Navigator>
<Bind.Screen
name="Bind"
component={() => <Text>Bind</Text>}
/>
</Bind.Navigator>
)
}
after that, you need to bind that code into the drawer component so the drawer component can render a whole navigator and give you the desire results
<Drawer.Screen
name="DriverDashboard"
component={BindedScreenComponent}
options={
{
drawerLabel: 'Home',
}
}
/>
snake: https://snack.expo.io/#nishargshah/react-navigation-5-boilerplate
My Old Explanation is correct for multiple screens bound in one screen but if you want only one screen with one drawer then you can use headerShown: true in your drawer options so, it will automatically show the header of the drawer.
Here is the example: https://snack.expo.io/#nishargshah/react-navigation-5-drawer
I have 2 different components AnimeList and MangaList that share similar logic, so I want to share code between them. Here is how I'm currently doing it:
const AnimeList = (props) => <AnimangaList isAnime={true} {...props} />;
const MangaList = (props) => <AnimangaList isAnime={false} {...props} />;
return (
<Tab.Navigator>
<Tab.Screen name="Anime" component={AnimeList} />
<Tab.Screen name="Manga" component={MangaList} />
</Tab.Navigator>
);
Is there a shorter or more convenient way to do this in React / React Native? ie. Something similar to function.bind()? This feels pretty hefty.
I usually go with static array when I need to have different values for few properties on large amount of components.
const pages = [
{
isAnime: true,
name: 'Anime',
},
{
isAnime: false,
name: 'Manga',
},
];
// mocks for some components
const MangaList = ({ isAnime }) => <div>{isAnime}</div>;
const Screen = ({ name, component: Component }) => (
<div>
{name}
<Component />
</div>
);
And then inside Tab.Navigator:
{pages.map((p, i) => (
<Screen key={i} name={p.name} component={(props) => <MangaList isAnime={p.isAnime} {...props} />} />
))}
I am trying to pass navigation params inside my navigation.js:
import { CATEGORIES, MEALS } from '../data/dummy-data';
<MealsNav.Screen
name="MealDetail"
component={MealDetailScreen}
options={({ route }) => {
const mealId = route.params.mealId;
const selectedMeal = MEAL.find(meal => meal.id === mealId);
return {
title: selectedMeal.title
},
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Favorite'
iconName='ios-menu'
onPress={() => console.log('Mark as the favorite')}
/>
</HeaderButtons>
),
}}
/>
This one doesn't work and I am not seeing the headerRight() being highlighted.
This part works though:
<MealsNav.Screen
name="CategoryMeals"
component={CategoryMealsScreen}
options={({ route }) => {
const catId = route.params.categoryId;
const selectedCategory = CATEGORIES.find((cat) => cat.id === catId);
return {
title: selectedCategory.title,
};
}}
/>
I just need the route + the other options to sit together.
The error says: error: Error: Unexpected token, expected ";" (92:31)
And the headerRight function did not executed since the icon did not shows up.
headerRight: () => (
What am I doing wrong here?
standart solution
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={({ navigation, route }) => ({
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#000"
/>
),
})
}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
Be carefully after return function called
because nothing works after return called
Start:
<NavigationContainer>
<Stack.Navigator>
{
isLoading ? <Stack.Screen name="AuthLoadingScreen" component={AuthLoadingScreen} />
: (
user ? (
<Stack.Screen name="AuthenticatedStack" component={AuthenticatedStack} options={{headerShown: false}} />
) : (
<Stack.Screen name="NotAuthenticatedStack" component={NotAuthenticatedStack} options={{headerShown: false}} />
)
)
}
</Stack.Navigator>
</NavigationContainer>
Authenticated stack:
const AuthenticatedStack = () => {
return (
<Drawer.Navigator initialRouteName="MainStack" drawerContent={props => <SideBar {...props} />}>
<Stack.Screen name="MainStack" component={MainStack} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
</Drawer.Navigator>
);
};
Main stack contain main screen with tab navigator and other navigators which I plan to navigate to from side menu buttons:
const MainStack = () => {
return (
<Stack.Navigator>
<Stack.Screen name="main" component={MainTabNavigator} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
<Stack.Screen options={({route}) => ({
title: getHeaderTitle(route),
})} name="Welcome" component={WelcomeStack} />
</Stack.Navigator>
);
};
Main tab navigator is just few tabs:
const MainTabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen
...
Home stack navigator:
const HomeStackNavigator = ({navigation, routes}) => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{
headerLeft: () => (),
}} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
};
Welcome stack:
const WelcomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} name="welcome" component={WelcomeScreen} />
</Stack.Navigator>
);
};
I use this two function to show hide header to avoud double headers on tabs and to set titles:
function shouldHeaderBeShown(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return false;
}
}
function getHeaderTitle(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return 'Home';
case 'Settings': return 'Settings';
case 'Welcome': return 'Welcome';
}
}
And here I got to the problem:
This line: route.state ? route.state.routes[route.state.index].name if I switch between tabs I get all correctly state property is there and it's fine titles are set.
But If I from drawer navigate to Welcome then route doesn't have state property so it always pick 'Home' for title.
I pass this by making this line to:
const routeName = route.state ? route.state.routes[route.state.index].name : route.name;
Then it is showing Welcome title.
But I don't understand is this workaround ok or this screens can be set in a better way?
But If I from drawer navigate to Welcome
How do you navigate from drawer? If you're navigating by passing screen param, you can do this:
function getActiveRouteName(route) {
// Access the tab navigator's state using `route.state`
const routeName = route.state
? // Get the currently active route name in the tab navigator
route.state.routes[route.state.index].name
: // If state doesn't exist, we need to default to `screen` param if available, or the initial screen
// In our case, it's "Home" as that's the first screen inside the navigator
route.params?.screen || 'Home';
return routeName;
}
Also seems the reason you even need this is because you have so many nested navigators. Maybe try to reduce nesting first? https://reactnavigation.org/docs/en/nesting-navigators.html#best-practices-when-nesting
I think you should use dispath not navigate.
In navigate, you can't get updated state if you navigate to that screen again.
import {StackActions} from 'react-navigation';
pushAction = StackActions.push({
routeName: 'FormsScreen',
params: {
id: result.id,
flag: true,
comments: result.note,
type: result.type,
date: result.created,
accountName: result.accountName,
accountId: result.accountId,
accountType: result.accountType,
bundleId: result.bundleId,
formAnswer: JSON.parse(result.formAnswer),
},
this.props.navigation.dispatch(pushAction);