Hide TopTabNavigator inside StackNavigator - reactjs

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.

Related

React context returning a null value despite default value

I keep getting the following error despite proving default values to my context.
TypeError: undefined is not an object (evaluating 'favoriteMealsCtx.ids')
Here's my context file:
import { createContext, useState } from "react";
export const FavoritesContext = createContext({
ids: [],
addFavorite: (id) => {
},
removeFavorite: (id) => {
}
});
function FavoritesContextProvider({children}) {
const [favoriteMealIds, setFavoriteMealIds] = useState([]);
function addFavorite(id) {
setFavoriteMealIds((currentFavIds) => [...currentFavIds, id]);
}
function removeFavorite(id) {
setFavoriteMealIds((currentFavIds) => currentFavIds.filter(mealId => mealId !== id))
}
const value = {
ids: favoriteMealIds,
addFavorite: addFavorite,
removeFavorite: removeFavorite
};
return (
<FavoritesContext.Provider value={value}>
{children}
</FavoritesContext.Provider>
)
}
export default FavoritesContextProvider;
Here's app.js using the <FavoritesContextProvider>:
export default function App() {
return (
<>
<StatusBar style="light"/>
<FavoritesContextProvider>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerStyle: {backgroundColor: '#351401'},
headerTintColor: 'white',
contentStyle: {backgroundColor: '#3f2f25'},
}}
>
<Stack.Screen
name="Drawer"
component={DrawerNavigator}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="MealsOverview" component={MealsOverviewScreen}/>
<Stack.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'About the Meal',
}}
/>
</Stack.Navigator>
</NavigationContainer>
</FavoritesContextProvider>
</>
);
}
And finally here's the MealDetail screen throwing the error when it loads:
function MealDetailScreen({route, navigation}) {
const favoriteMealsCtx = useContext(FavoritesContext);
console.log(favoriteMealsCtx)
const mealId = route.params.mealId;
const mealIsFavorite = favoriteMealsCtx.ids.includes(mealId) // Here's the error
const selectedMeal = MEALS.find((meal) => meal.id === mealId);
function changeFavoriteStatusHandler() {
if ( mealIsFavorite ) {
favoriteMealsCtx.removeFavorite(mealId)
} else {
favoriteMealsCtx.addFavorite(mealId)
}
}
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => {
return (
<IconButton
icon={mealIsFavorite ? 'star' : 'star-outline'}
color="white"
onPress={changeFavoriteStatusHandler}
/>
);
},
});
}, [navigation, changeFavoriteStatusHandler]);
return (
<ScrollView style={styles.rootContainer}>
<Image style={styles.image} source={{uri: selectedMeal.imageUrl}}/>
<Text style={styles.title}>{selectedMeal.title}</Text>
<MealDetails
duration={selectedMeal.duration}
complexity={selectedMeal.complexity}
affordability={selectedMeal.affordability}
textStyle={styles.detailText}
/>
<View style={styles.listOuterContainer}>
<View style={styles.listContainer}>
<Subtitle>Ingredients</Subtitle>
<List data={selectedMeal.ingredients}/>
<Subtitle>Steps</Subtitle>
<List data={selectedMeal.steps}/>
</View>
</View>
</ScrollView>
);
}
I don't understand why const favoriteMealsCtx = useContext(FavoritesContext); is returning null when it should have been initialized already.

The action 'NAVIGATE' with payload {"name":"RestaurantDetail"} was not handled by any navigator. Do you have a screen named 'RestaurantDetail'?

I could not fix this bug. It says RestaurantDetail was not handled by any navigator and asks me if I do you have a screen named RestaurantDetail? The idea is to get a restaurant detail card from the bottom of the app. Any help will be appreciated
export const RestaurantsScreen = ({navigation}) => {
const {isLoading, restaurants} = useContext(RestaurantContext);
return (
<SafeArea>
{isLoading && (
<LoadingContainer>
<Loading
size={50}
style={{marginLeft: -25}}
animating={true}
color={Colors.blue300}
/>
</LoadingContainer>
)
}
<Search />
<RestaurantList
data={restaurants}
renderItem={({item}) => {
return(
<Pressable onPress={() => navigation.navigate("RestaurantDetail")}>
<Spacer position="bottom" size="large">
<RestaurantInfoCard restaurant={item} />
</Spacer>
</Pressable>
)}
}
keyExtractor={(item) => item.name}
/>
</SafeArea>
)
};
const RestaurantStack = createStackNavigator();
export const RestaurantsNavigator = () => {
return (
<RestaurantStack.Navigator headerMode="none">
<RestaurantStack.Screen
name="Restaurants"
component={RestaurantsScreen}
/>
<RestaurantStack.Screen
name="RestaurantDetail"
component={() => <Text>Restaurant Detail</Text>}
/>
</RestaurantStack.Navigator>
);
};

Can´t use navigation && props in React-Native?

I am working on a React-Native Project and found an issue..
Everything is working fine except my HomeStackScreen, i want to pass data via props, but also want to navigate to open a drawer
const HomeStackScreen = (props, navigation) => (
console.log("HOME STACK: " + props.studentData),
<HomeStack.Navigator headerMode="screen" studentData={props.studentData}>
<HomeStack.Screen name = "Home" children={() => <HomeScreen studentData={props.studentData} />} options={{
title:'Home',
headerStyle: {
backgroundColor: '#e67e22',
},
headerRight: () => (
<Icon.Button name="build" size={30}
backgroundColor="#e67e22" color="white" paddingLeft= {15} onPress= {() => navigation.
openDrawer()}></Icon.Button>
)
}}/>
</HomeStack.Navigator>
);
Here is my code, but when I write
const HomeStackScreen = (props) => (
or
const HomeStackScreen = ({navigation}) => (
everything works, but when I want to use both I get an error, telling me one of them is not a function
You should use them like this :
const HomeStackScreen = (props) => {
const {navigation,studentData} = props ;
return ...
}
Pass props as an object and:
const HomeStackScreen = ({navigation, studentData}) => (
console.log("HOME STACK: " + studentData),
<HomeStack.Navigator headerMode="screen" >
<HomeStack.Screen name = "Home" children={() => <HomeScreen studentData={studentData} />} options={{
title:'Home',
headerStyle: {
backgroundColor: '#e67e22',
},
headerRight: () => (
<Icon.Button name="build" size={30}
backgroundColor="#e67e22" color="white" paddingLeft= {15} onPress= {() => navigation.
openDrawer()}></Icon.Button>
)
}}/>
</HomeStack.Navigator>
);

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

Passing params to tab navigator React Navigation 5

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

Resources