Passing params to tab navigator React Navigation 5 - reactjs

I’m using materialTopTabs and it seems like this loads all the screens in the navigator once its mounted. I have a screen List and inside it a tab navigator with 2 screens: Posts and Users. These two screen both depend on params passed from List. However, i am only able to pass params to one of the screens using this method:
navigation.navigate('PostsTabNav', {
params: {
network: item,
},
screen: 'NetworkPosts' //or NetworkUsers
});
I have tried to pass the params to my navigator directly by doing this:
navigation.navigate('PostsTabNav', {
network: item
});
The first option only allows me to pass to one screen. The second option allows me to access the params inside the navigator like this:
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = (props) => {
const temp = props.route.params.network; //params here
return (
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} />
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} />
</PostsTabNav.Navigator>
);
};
Is there a way to pass temp to both my screens? If not is there a better way to handle this situation?
Here's the code for the StackNavigator
const NetworkListStackNav = createStackNavigator();
export const NetworksListNavigator = () => {
return (
<NetworkListStackNav.Navigator>
<NetworkListStackNav.Screen name="List" component={ListScreen} />
<NetworkListStackNav.Screen name="PostsTabNav" component={PostsMainNav} />
</NetworkListStackNav.Navigator>
);
};

Pass params to the navigator and then expose it to the tabs using React Context.
Create a context in a separate file which you can import in both your navigator and screens:
export const NetworkContext = React.createContext();
Then provide the params in the context:
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = ({ route }) => {
return (
<NetworkContext.Provider value={route.params.network}>
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} />
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} />
</PostsTabNav.Navigator>
</NetworkContext.Provider>
);
};
In your screen component, use the context:
const network = React.useContext(NetworkContext);
Also see https://reactnavigation.org/docs/hello-react-navigation#passing-additional-props

You can set initial params to your screens.
const PostsTabNav = createMaterialTopTabNavigator();
const PostsMainNav = (props) => {
const temp = props.route.params.network
return (
<PostsTabNav.Navigator>
<PostsTabNav.Screen name="NetworkPosts" component={NetworkPostsScreen} initialParams={network:temp}/>
<PostsTabNav.Screen name="NetworkUsers" component={NetworkUsersScreen} initialParams={network:temp}/>
</PostsTabNav.Navigator>
);
};

const Stack = createStackNavigator();
function AppNavigator(props) {
const { cartCount } = props;
return (
<NavigationContainer>
<Stack.Screen {...props} name="MainScreen" component={() => <MyTabs cartCounts={cartCount}></MyTabs>} options={{ headerShown: false }} countProp={cartCount} initialParams={{ 'count': cartCount }} />
</Stack.Navigator>
</NavigationContainer>
)
}
function mapStateToProps(state) {
return {
cartCount: state.cart.cartCount,
}
}
export default connect(mapStateToProps, {})(AppNavigator);
And your tab component like
function MyTabs(props) {
let cartCount = props.cartCounts;
return (
<BottomTab.Navigator
screenOptions={{
tabBarStyle: { position: 'absolute' },
tabBarStyle: styles.tabbarStyle
}}>
<BottomTab.Screen name="CartScreen" component={CartScreen} options={{ headerShown: false }}
options={{
headerShown: false,
tabBarIcon: ({ focused, tintColor }) => (
<View style={styles.cartIconView}>
<Image
square
source={bottomCart}
/>
<Badge style={[GlobalStyles.badge, styles.count, {
}]}>
<Text style={[GlobalStyles.badgeText, styles.countText]}>{cartCount}</Text>
</Badge>
</View>
),
}} />
</BottomTab.Navigator >
);

Related

Passing parameter to bottom tab component in react native navigation 6

i have code like below
const Project = ({ navigation, route }) => {
const id = route.params.itemId;
return (
<Stack.Navigator >
<Stack.Screen
name="ProjectMain"
component={TopNav}
initialParams={{ id: id }}
/>
</Stack.Navigator>
);
};
Now how do i pass initialParams to my component TopNav
my TopNav is like below
const TopNav = (props) => {
console.log(props);
console.log(props.route);
return (
<>
//my tab screens
</>
);
};
I am not able to get the params here , its always undefined ,how do i pass something to my TopNav component , thank you
This looks correct to me.
Can you double check that id (line 2 in Project) is not undefined?
What is the output of props.route? It should be an object with key, name, params, and path
Can you try using:
const TopNav = () => {
const route = useRoute()
console.log(route);
return (
<>
//my tab screens
</>
);
};
Use options.
<Stack.Screen
name="ProjectMain"
component={TopNav}
options={{
id: 'Awesome app',
}}
/>

Hide TopTabNavigator inside StackNavigator

I am using createMaterialTopTabNavigator from react-navigation and each Tab has StackNavigator. How can I hide Top tabs after opening Stack screen? Currently using Expo managed with Redux.
Here's the code:
const favStack = createStackNavigator();
const loserStack = createStackNavigator();
const gainerStack = createStackNavigator();
const activeStack = createStackNavigator();
const Tab = createBottomTabNavigator();
const MarketsTab = createMaterialTopTabNavigator();
export default class App extends Component {
state = { loading: true };
favStackScreen = () => (
<favStack.Navigator>
<favStack.Screen name="Favorites" component={Favorites} />
<favStack.Screen name="Stock" component={Stock} />
</favStack.Navigator>
);
loserStackScreen = () => (
<loserStack.Navigator tabBarVisible="false">
<loserStack.Screen name="Losers" component={Losers} />
<loserStack.Screen name="Stock" component={Stock} />
</loserStack.Navigator>
);
gainerStackcreen = () => (
<gainerStack.Navigator headerMode="float">
<gainerStack.Screen name="Gainers" component={Gainers} />
<gainerStack.Screen name="Stock" component={Stock} />
</gainerStack.Navigator>
);
activeStackScreen = () => (
<activeStack.Navigator>
<activeStack.Screen name="MostActive" component={MostActive} />
<activeStack.Screen name="Stock" component={Stock} />
</activeStack.Navigator>
);
MarketsTabScreen = () => (
<MarketsTab.Navigator
style={{
paddingTop:
Platform.OS === "ios"
? Constants.statusBarHeight
: StatusBar.currentHeight,
}}
>
<MarketsTab.Screen
name="Losers"
component={this.loserStackScreen}
></MarketsTab.Screen>
<MarketsTab.Screen
name="Gainers"
component={this.gainerStackcreen}
></MarketsTab.Screen>
<MarketsTab.Screen
name="MostActive"
component={this.activeStackScreen}
></MarketsTab.Screen>
</MarketsTab.Navigator>
);
async componentDidMount() {
await Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
...Ionicons.font,
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <AppLoading style={styles.container} />;
}
return (
<Provider store={store.store}>
<PersistGate loading={null} persistor={store.persistor}>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Favorites" component={this.favStackScreen} />
<Tab.Screen name="Markets" component={this.MarketsTabScreen} />
</Tab.Navigator>
</NavigationContainer>
</PersistGate>
</Provider>
);
}
}
For managing state I am using Redux and all my components are class(so can not use Hooks).
Components being used: (Gainers, Losers, MostActive as Top Tabs) and Favorites as Bottom Tabs. All of them should have access to Stock component.
In your case you can set the tabBar prop of your MarketsTab navigator to a function that returns null like this:
MarketsTabScreen = () => (
<MarketsTab.Navigator
tabBar={() => null}
style={{
paddingTop:
Platform.OS === 'ios'
? Constants.statusBarHeight
: StatusBar.currentHeight,
}}>
<MarketsTab.Screen name="Losers" component={this.loserStackScreen} />
<MarketsTab.Screen name="Gainers" component={this.gainerStackcreen} />
<MarketsTab.Screen name="MostActive" component={this.activeStackScreen} />
</MarketsTab.Navigator>
);
You can still swipe to the screens in the tab navigator, but the top tab bar won't be shown.

React Navigation 5: How can I put route along with other options

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

Why route object sometimes have state and sometimes don't - in react-navigation v5

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);

How to pass props between functions within a React component

I'm looking to pass a prop to a component in the header of the navigation option. Is there a way to pass itemId to SaveButton from within the component without using something like Context API or Redux?
export default PostingDetail = ({ navigation }) => {
const itemId = navigation.getParam('itemId', null)
return (
<SafeAreaView >
</SafeAreaView>
)
}
PostingDetail.navigationOptions = ({ navigation }) => ({
title: "Posting Detail",
headerRight: (
<PleaseSignIn navigation={navigation} >
<SaveButton id={itemId} />
</PleaseSignIn>
)
})
You can pass param as below :
export default PostingDetail = ({ navigation }) => {
const itemId = navigation.getParam('itemId', null);
navigation.setParams({ itemId }); // Pass itemId to navigation state
return (
<SafeAreaView >
</SafeAreaView>
)
}
And then you can get itemId as below :
PostingDetail.navigationOptions = ({ navigation }) => ({
title: "Posting Detail",
headerRight: (
<PleaseSignIn navigation={navigation} >
<SaveButton id={navigation.state.params.itemId} />
</PleaseSignIn>
)
})

Resources