how to use custom component in header for createMaterialTopTabNavigator - react-native

Its just text if you set "name" props of tab.Screen
as name="Favourites",
I need to do something like name={} ... of course its not the case..
component:
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-tabs";
import { Text } from "react-native";
const Tab = createMaterialTopTabNavigator();
function CupboardNavigation() {
return (
<Tab.Navigator
screenOptions={{
swipeEnabled: false
}}
>
<Tab.Screen
name="Favourites"//I wish this could be component instead of text..
component={Favourites}
/>
</Tab.Navigator>
);
}
How can I achive this?

Related

Dynamic id transition page for each individual element of the array

I need your help. I have a separate file arrayOfBoxes, which contains an array of elements with fields id and title. There is also a Boxes component in which I iterate this array and there is a BoxesDetailsComponent component in which I want to have a transition to the details of each of the objects. The fact is that I use react-navigation together with bottom-tabs and unfortunately I don't know how to do it. Can you help with this task: how to use the details button in the Boxes component to switch to the BoxesDetailsComponent component by different id? Thanks a lot
arrayOfBoxes.js
export const arrayOfBoxes = [
{id: 1, title: "Box 1"},
{id: 2, title: "Box 2"},
{id: 3, title: "Box 3"} ]
Boxes.js
import {Button, FlatList, StyleSheet, Text, View} from 'react-native';
import {arrayOfBoxes} from "../array/arrayOfBoxes";
import {useState} from "react";
const Boxes = () => {
const [autoBoxes, setAutoBoxes] = useState(arrayOfBoxes);
return (<View>
<FlatList data={autoBoxes} renderItem={({item}) => {
return <View>
<Text>{item.title}</Text>
<Button title={'Details'} onPress={() => {//Go to BoxesDetails}}/>
</View>}}/>
</View>)
export default Boxes;
BoxesDetailsComponent.js
import { Text, View } from 'react-native';
const BoxesDetailsComponent = () => {
return (<View>
<Text>Boxes Details</Text>
</View>)
}
export default BoxesDetailsComponent;
App.js
import { StyleSheet } from 'react-native';
import {createBottomTabNavigator} from "#react-navigation/bottom-tabs";
import {NavigationContainer} from "#react-navigation/native";
import Boxes from "./components/Boxes";
import VacuumCleaner from "./components/VacuumCleaners";
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name={'Бокси'} component={Boxes}/>
<Tab.Screen name={'Пилососи'} component={VacuumCleaner}/>
</Tab.Navigator>
</NavigationContainer>);
}
You can get default navigation in the component and using this navigation you can navigate to the next screen. Please check the below code:
import { Button, FlatList, StyleSheet, Text, View } from 'react-native';
import { arrayOfBoxes } from "../array/arrayOfBoxes";
import { useState } from "react";
const Boxes = ({ navigation }) => {
const [autoBoxes, setAutoBoxes] = useState(arrayOfBoxes);
return (<View>
<FlatList data={autoBoxes} renderItem={({ item }) => {
return <View>
<Text>{item.title}</Text>
<Button title={'Details'} onPress={() => navigation.navigate('DetailsScreen')} />
</View>
}} />
</View>)
}
export default Boxes;
Your Navigator like below:
function Home() {
return (
<Tab.Navigator>
<Tab.Screen name={'Бокси'} component={Boxes}/>
<Tab.Screen name={'Пилососи'} component={VacuumCleaner}/>
</Tab.Navigator>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Stack.Screen name="BoxesDetailsComponent" component={BoxesDetailsComponent} />
</Stack.Navigator>
</NavigationContainer>
);
}

error while updating property 'title' of a view managed by: RNScreenStackHeaderConfig (react native)

My app has a home screen (HomeScreen.js) with a bottom tab navigator, and it is nested within a stack navigator in App.js. Whenever I try and navigate to the profile screen from the home screen I get the following error:
This error only occurs when I try and set headerTitle to a custom component in App.js. Does anyone know how I can set the header of ProfileScreen to a custom component correctly?
My navigation structure looks like this:
Stack.Navigator
Tab.Navigator
Food (screen)
Friends (screen)
Lists (screen)
Profile (screen)
I am able to set a custom component as the header in Tab.Navigator, but not in Profile
Let me know if more information is needed.
App.js:
import React from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "react-native-screens/native-stack";
import HomeArea from "./screens/HomeScreen";
import ProfileScreen from "./screens/ProfileScreen";
import NavBar from "./componets/NavBar";
import BackSVG from "./assets/BackSVG";
import { Text, View } from "react-native";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="HomeArea"
component={HomeArea}
options={{ headerShown: false }}
/>
<Stack.Screen
name="ProfileScreen"
component={ProfileScreen}
options={{
headerTitle: props => {
return <Text>Test</Text>;
}
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
HomeScreen.js:
import * as React from "react";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import FoodScreen from "../screens/FoodScreen";
import FriendsScreen from "../screens/FriendsScreen";
import Lunchlists from "../screens/Lunchlists";
import { StyleSheet, View } from "react-native";
import FoodSVG from "../assets/FoodSVG";
import ListSVG from "../assets/ListSVG";
import FriendsSVG from "../assets/FriendsSVG";
import NavBar from "../componets/NavBar";
import UserSVG from "../assets/UserSVG";
const Tab = createBottomTabNavigator();
export default function HomeScreen({ navigation }) {
return (
<Tab.Navigator
screenOptions={{
tabBarStyle: styles.navBar,
tabBarActiveTintColor: "#fff",
tabBarShowLabel: false,
header: () => (
<NavBar
symbolRight={UserSVG}
symbolLeft={View}
rightPressEvent={() => navigation.navigate("ProfileScreen")}
/>
)
}}>
<Tab.Screen
name="Food"
component={FoodScreen}
options={{
tabBarLabel: "Food",
tabBarIcon: ({ color, size }) => {
return <FoodSVG width={size} height={size} color={color} />;
}
}}
/>
<Tab.Screen
name="Friends"
component={FriendsScreen}
options={{
tabBarLabel: "Friends",
tabBarIcon: ({ color, size }) => {
return <FriendsSVG width={size} height={size} color={color} />;
}
}}
/>
<Tab.Screen
name="Lunchlists"
component={Lunchlists}
options={{
tabBarLabel: "Lunchlists",
tabBarIcon: ({ color, size }) => {
return <ListSVG width={size} height={size} color={color} />;
}
}}
/>
</Tab.Navigator>
);
}
const styles = StyleSheet.create({
navBar: {
backgroundColor: "#5A4664"
}
});
I imported createNativeStackNavigator from the wrong package. In App.js I instead imported it using import { createNativeStackNavigator } from "#react-navigation/native-stack";, which solved my problems.

Received warning when Hide Bottom Tab Navigation in React Native

I received warning when tried to hide bottom nav in specific screen. The warning is 'Cannot update a component from inside the function body of a different component', So what I'm trying to do is that I've a home screen and when I navigate to detail screen the bottom nav is going to disappear/hidden. my code as below:
ProductNavigation.js my stack navigation
import 'react-native-gesture-handler';
import React, {useState, useEffect} from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { createStackNavigator } from '#react-navigation/stack'
import MainScreen from '../screen/MainScreen'
import DetailScreen from '../screen/DetailScreen'
const Stack = createStackNavigator();
const ProductNavigation = ({navigation, route}) => {
if (route.state) {
navigation.setOptions({
tabBarVisible: route.state.index > 0 ? false: true
})
}
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={MainScreen} />
<Stack.Screen name="Detail" component={DetailScreen} options = {({ route }) => ({title:
route.params.productName })}/>
</Stack.Navigator>
)
}
export default ProductNavigation;
BottomTabNav.js my bottom navigation
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import Ionicons from 'react-native-vector-icons/Ionicons'
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs'
import ProductNavigation from './ProductNavigation'
import SettingScreen from '../screen/SettingScreen'
const BottomTab = createBottomTabNavigator();
const BottomTabNav = ({ navigation, route }) => {
return (
<BottomTab.Navigator>
<BottomTab.Screen
name="Home"
component={ProductNavigation}
options={{
tabBarLabel: "Home",
tabBarIcon:({color, size}) => (
<Ionicons name="home-outline" color={color} size={size} />)
}} />
<BottomTab.Screen
name="Settings"
component={SettingScreen}
options={{
tabBarLabel: "Settings",
tabBarIcon: ({color, size}) => (
<Ionicons name="settings-outline" color={color} size={size} />)
}} />
</BottomTab.Navigator>
)
}
export default BottomTabNav
yes it got hidden in detail screen, but why there is a warning? where should I edit or change my code?
Looking at the docs for navigation.setOptions (https://reactnavigation.org/docs/navigation-prop/#setoptions) there they put the logic in the useLayoutEffect hook.
try changing:
import React from 'react'
...
if (route.state) {
navigation.setOptions({
tabBarVisible: route.state.index > 0 ? false: true
})
}
to
import React, { useLayoutEffect } from 'react'
...
...
useLayoutEffect(() => {
if (route.state) {
navigation.setOptions({
tabBarVisible: route.state.index > 0 ? false: true
})
}
}, [navigation, route])

Correct way of adding dynamic tab navigation screens: createMaterialTopTabNavigator

Code is working just fine. All I want to know is if this is the correct way of doing it.
I want tabs based on values in array so I did this:
import React from "react";
import { createMaterialTopTabNavigator } from "#react-navigation/material-top-tabs";
const Tab = createMaterialTopTabNavigator();
function ProductListNavigator({ route }) {
const { subCategories, selectedSubcategory } = route.params;
return (
<Tab.Navigator
lazy={true}
lazyPreloadDistance={3}
>
{subCategories.map((subCategory) => (
<Tab.Screen
key={subCategory.id}
name={subCategory.name}
component={AppProductList}
options={{ tabBarLabel: subCategory.name }}
initialParams={{ subCategory }}
/>
))}
</Tab.Navigator>
);
}
export default ProductListNavigator;
Is this going to be inefficient? Or is there a better way of doing it?
I am using React navigation v5

How to use navigation.navigate from a component outside the stack.navigation

I have an application using React native where I am using react-navigation (5.2.9).
I built a Stack.Navigator where I've got my screens but I want the Footer component to be outside so it renders in all screens. The problem is, I can't navigate from the footer, which is what I need to do as the footer has a few buttons that should be changing the screen:
const Stack = createStackNavigator();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer>
<Header />
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false
}}
/>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
headerShown: false
}}
/>
</Stack.Navigator>
<Footer />
</NavigationContainer>
</Provider>
);
};
How do I pass the navigation prop to the footer component?
Try this:
Create a new file named: RootNavigation.js
// RootNavigation.js
import * as React from 'react';
export const navigationRef = React.createRef();
export function navigate(name, params) {
navigationRef.current?.navigate(name, params);
}
// file where you have the navigation
import {navigationRef} from './path/to/RootNavigation';
<NavigationContainer ref={navigationRef}>
.....
<Footer />
</NavigationContainer>
And Footer.js should be something like this:
// Footer.js
import React from 'react';
import {View, Button} from 'react-native';
import * as RootNavigation from './path/to/RootNavigation';
const Footer= () => {
return (
<View>
<Button
title={'Go to'}
onPress={() =>
RootNavigation.navigate('screenName ', {userName: 'Lucy'})
}
/>
</View>
);
};
export default Footer;
For more info you can read the documentation. https://reactnavigation.org/docs/navigating-without-navigation-prop/
The components outside of the Stack.Screen components do not receive the navigation prop. So in this case, you have to get the NavigationContainer using refs in your footer component, as follows:
Create a ref with const myRef = React.createRef()
pass it to the <NavigationContainer ref={myRef}>, and
use that ref to navigate, myRef.current?.navigate(name, params).
It is all explained here. The docs here separate the creation of the ref in a new file, to allow you to import the ref without dependency loops/ issues. As the docs state, you can now call RootNavigation.navigate('ChatScreen', { userName: 'Lucy' }); in any js module, not just in a react component.
In your case though, you don't need the ref in separate file.
const Stack = createStackNavigator();
const navigationRef = React.createRef();
const App = () => {
return (
<Provider store={store}>
<NavigationContainer ref={navigationRef}>
<Header />
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown: false
}}
/>
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
headerShown: false
}}
/>
</Stack.Navigator>
<Footer navigationRef={navigationRef}/>
</NavigationContainer>
</Provider>
);
};
And in <Footer/> use navigationRef.current?.navigate(name, params)
From the documentation.
https://reactnavigation.org/docs/connecting-navigation-prop/
import * as React from 'react';
import { Button } from 'react-native';
import { useNavigation } from '#react-navigation/native';
function GoToButton({ screenName }) {
const navigation = useNavigation();
return (
<Button
title={`Go to ${screenName}`}
onPress={() => navigation.navigate(screenName)}
/>
);
}
I ended up building a component called screen that will just wrap the content of screen and render the header/footer based on props:
import React from 'react';
import { View } from 'native-base';
import style from './style';
import Footer from '../../footer';
import Header from '../../header';
const Screen = ({
footer,
header,
children,
navigation
}) => (
<View style={style.screen}>
{ header && <Header navigation={navigation} />}
{ children }
{ footer && <Footer navigation={navigation} />}
</View>
);
export default Screen;
And wrapping the screens of my apps like this:
<Screen header footer navigation={navigation}>
... screen content
</Screen>
I feel like it is the best way if I can't sort that problem out.
A simple trick to extract navigation out of screenOptions.
function NavWrapper() {
const navigatorRef = useRef<any>();
<Tab.Navigator
screenOptions={({ navigation: nav }) => navigatorRef.current = nav}
>
<Tab.Screen name="screen1" />
</Tab.Navigator>
}
I solve this problem with a global state using React context API:
// When enter in the HomeScreen:
const { setGlobalNavigation, globalNavigation } = useGlobalContext();
const navigation = useNavigation<RemindersNavigationProp>();
useEffect(() => {
if (setGlobalNavigation && !globalNavigation) setGlobalNavigation(navigation);
}, [setGlobalNavigation, globalNavigation, navigation]);
// When want to use:
const { globalNavigation } = useGlobalContext();
const handleNavigate = () =>
globalNavigation.navigate("contactsStack", { screen: "newContact" });