How to pass props to a screen component in react navigation - react-native

I have a navigator that looks like this and I'm trying to pass informations to all the tabs below it.
import {createMaterialTopTabNavigator} from '#react-navigation/material-top-tabs';
const Tab = createMaterialTopTabNavigator();
<Tab.Navigator
swipeEnabled={false}
initialRouteName="TabMapScreen"
screenProps={user} // I've tried this
initialLayout={{width: Dimensions.get('window').width}}
>
<Tab.Screen
name="TabMapScreen"
component={PostsTab}
/>
<Tab.Screen
name="TabMapScreen"
component={() => <PostsTab props={user} />} // also tried this
/>
</Tab.Navigator>
what's the correct solution to passing props to a screen, straight from the navigator?

You can use the initialParams
<Tab.Screen
name="TabMapScreen"
component={PostsTab}
initialParams={{userData: user}} //User data is just an alias
/>

Just try this
<Tab.Screen name="TabMapScreen">
{props => <A123 {...props} props={user} />}
</Tab.Screen>
I think it is what you want !

There is two ways to do so:
Route params
This is if you need to change the props/parameters dynamically at navigate time:
function HomeScreen({ navigation }) {
return <View>
<Button
title="Go to Details"
onPress={() => {
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
</View>
}
function DetailsScreen({ route, navigation }) {
/* 2. Get the param */
const { itemId, otherParam } = route.params
...
}
Initial params
This way is if you need to pass props/params when the route is defined
<Stack.Screen
name="Details"
component={DetailsScreen}
initialParams={{ itemId: 42 }}
/>
Full documentation here

Related

React Native navigation 6 (Expo) - how can I toggle the drawer tab from the header of my Tabs navigation?

Hi I have react native (using expo) navigation 6. I am trying to have Tabs (Bottom) navigation and drawer navigation. I want to have the drawer nav hamburger menu inside the header. As far as I can see its being done having the following
headerLeft: (props) => {
return <Button
title="yes"
onPress={() => navigation.toggleDrawer() } />
}
either inside screenOptions or options (of the Tabs navigation). I kept it in screenOptions as I want it to be visible on all screens. However whatever I tried so far I am always getting an err saying either
undefined is not an object (wenn I pass in an object to screenOption (please see full code below)
or
navigation.ToggleDrawer is not a function - when I pass a function to screenOptions (please see full code below). I cannot find any solution or understand what I am doing wrong. Any help would be great!
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name="Test" component={Test} />
<Drawer.Screen name="s" component={SearchScreen} />
</Drawer.Navigator>
)
}
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator
screenOptions={({ navigation }) => ({ //here I get "undefined is not an object"
headerLeft: (props) => {
return <Button
title="yes"
onPress={() => navigation.toggleDrawer() } />
}
})}
/*screenOptions={{ // here I would get "navigation.ToggleDrawer is not a function"
}}*/
>
<Tab.Screen name="Home" component={HomeScreen}/>
<Tab.Screen name="Tab1" component={Tab1} />
<Tab.Screen name="Tab2" component={Tab2} />
</Tab.Navigator>
);
}
const Navigator = () => {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
)
}
export default Navigator;

react navigation drawer reopen after navigate to screen

I'm working on a simple app and have now added drawer navigation, but I'm having a weird problem with the drawer that opens again after navigating to the screen from the drawer content.
PlayerZone is a BottomTabNavigation that contains Profile and other screens.
I tried to dispatch close drawer action before and after navigate to screen, also I tried requestAnimationFrame but nothing helps.
how it looks like
DrawerScreens:
<Animated.View style={[styles.stack, style]}>
<Stack.Navigator
screenOptions={{
header: () => null,
}}>
<Stack.Screen name="PlayerZone" component={PlayerZone} />
</Stack.Navigator>
<GameBottomNavigation />
</Animated.View>
Drawer:
<DrawerItem
label="Profile"
style={styles.item}
labelStyle={styles.itemLabel}
onPress={() => {
props.navigation.navigate('PlayerZone', {
screen: 'Profile',
id: null,
});
}}
/>
<DrawerItem
label="Items"
style={styles.item}
labelStyle={styles.itemLabel}
onPress={() => {
props.navigation.navigate('PlayerZone', {
screen: 'Profile',
id: 'some-id',
});
}}
/>
PlayerZone:
<Tab.Navigator
initialRouteName="Profile"
screenOptions={{
header: () => null,
}}>
<Tab.Screen name="Profile" component={Profile} />
{!peeking && <Tab.Screen name="Items" component={Items} />}
</Tab.Navigator>
Items:
import React, {FC} from 'react';
import {BottomTabScreenProps} from '#react-navigation/bottom-tabs';
import Block from '../../components/Block';
import Text from '../../components/Text';
import {PlayerZoneTabParamList} from '.';
type Props = BottomTabScreenProps<PlayerZoneTabParamList, 'Items'>;
const Items: FC<Props> = () => {
return (
<Block middle center>
<Text>Items</Text>
</Block>
);
};
export default Items;
So after some time trying everything I could think of I found solution (kind of). In drawer navigator I had props useLegacyImplementation set to false because I have configured reanimated 2. I removed this props and everything start working properly.

UI Kitten and initialRouteName

I am trying to set a specific tab to start the application. UI kitten's navigation, starts up with the first tab, and actually I want the tab that is in the second position to be the first to appear to the user. I can not find how to set the initialRoute withtin UI kittens Bottom tab bars. I post some of my code so it can be clear:
const { Navigator, Screen } = createBottomTabNavigator();
const BottomTabBar = ({ navigation, state }) => (
<View>
<Divider />
<BottomNavigation
appearance="noIndicator"
selectedIndex={state.index}
onSelect={(index) => navigation.navigate(state.routeNames[index])}
>
<BottomNavigationTab title="screen1" icon={icon1} />
<BottomNavigationTab title="screen2" icon={icon2} />
<BottomNavigationTab title="screen3" icon={icon3} />
</BottomNavigation>
</View>
);
export const BottomTabsNavigator = () => (
<Navigator tabBar={(props) => <BottomTabBar {...props} />}>
<Screen name="screen1" component={Screen1}/>
<Screen name="screen2" component={Screen2}/>
<Screen name="screen3" component={Screen3} />
</Navigator>
);
export const AppNavigator = () => {
return (
<SafeAreaView>
<NavigationContainer>
<Navigator headerMode='none' >
<Screen name={'BottomTabs'} component={BottomTabsNavigator} />
</Navigator>
</NavigationContainer>
</SafeAreaView>
)
};
I've tried in multiple positions but it doesn't seem to work.
Within the following component:
<Navigator tabBar={(props) => <BottomTabBar {...props} />}>
You need to use the following prop: initialRouteName, like so:
<Navigator initialRouteName={'namehere'} tabBar={(props) => <BottomTabBar {...props} />}>
This is telling the navigator what screen to start on when initially rendered! Hope this helps.
For anyone outside of this question that has landed on this page, you can specify what screen you want the navigator to "start" on by using the prop: initialRouteName. This then tells the navigator to use this as your landing page!

navigation.navigate on a button in headerRight of the Stack Navigator

Firstly, I have the same issue as Button linking in headerRight. But their solution was simply using functional over component. I cannot simply switch to functional code as I need to use componentDidMount, so I really need solution for a component based headerRight navigation usage.
Stack
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabs}
options={{
headerRight: ({ navigation }) => (
<View>
<Button
onPress={() => navigation.navigate('Profile')}
</Button>
</View>
),
}}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}
BottomTabs
const BottomTabs = createBottomTabNavigator();
function MyTabs() {
return (
<BottomTabs.Navigator
...
This will bring an error that navigation is not available there. Okay that's right, as you cannot use navigation directly in the definition of the Stack Navigator.
Even using:
headerRight: () => {
return <ProfileButtonScreen/>
},
did not help as on that component I still not have the navigation available.
This is too less info but is already going in the right direction. And finally this gave me the idea about misusing the BottomTabs for the defining of the headerRight.
Stack
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabs}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
);
}
BottomTabs
const BottomTabs = createBottomTabNavigator();
function MyTabs({ navigation, route }) {
navigation.setOptions({
headerRight: () => (
<View>
<Button
onPress={() => navigation.navigate('Profile')}
title="To Profile"
>
</Button>
</View>
),
});
return (
<BottomTabs.Navigator
...
This will now let you have a clickable button on stack navigation header.

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.