When I try to access the Drawer the app closes and does not show me any type of error, I have not been able to solve the problem and be able to view the Draw.
This function is the one that generates problems for me, it should be noted that I am new to react and it is most likely that I have an error in the navigation configuration.
const LoggedInNavigator = () => (
<LoggedInStack.Navigator>
<LoggedInStack.Screen name="Home" component={Home} options={homeOptions} />
</LoggedInStack.Navigator>
);
I share the complete navigation file
import * as React from 'react';
import { NavigationContainer, Theme } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { useSelector } from 'react-redux';
import { navigationRef } from './NavigationService';
import Login from 'app/screens/Login';
import Home from 'app/screens/Home';
import ForgotPassword from 'app/screens/ForgotPassword';
import ThemeController from '../components/ThemeController';
import { StatusBar, Text } from 'react-native';
import { ILoginState } from 'app/models/reducers/login';
const Stack = createStackNavigator();
const AuthStack = createStackNavigator();
const LoggedInStack = createDrawerNavigator();
const homeOptions = {
title: 'Home',
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => <ThemeController />,
};
interface IState {
loginReducer: ILoginState;
}
interface IProps {
theme: Theme;
}
const AuthNavigator = () => {
const isLoggedIn = useSelector(
(state: IState) => state.loginReducer.isLoggedIn,
);
return (
<AuthStack.Navigator>
<Stack.Screen
name="Login"
component={Login}
options={{
// When logging out, a pop animation feels intuitive
// You can remove this if you want the default 'push' animation
animationTypeForReplace: isLoggedIn ? 'push' : 'pop',
headerRight: () => <ThemeController />,
}}
/>
<Stack.Screen
name="ForgotPassword"
component={ForgotPassword}
options={{
// When logging out, a pop animation feels intuitive
// You can remove this if you want the default 'push' animation
animationTypeForReplace: isLoggedIn ? 'push' : 'pop',
headerRight: () => <ThemeController />,
}}
/>
</AuthStack.Navigator>
);
};
//This component is the problem, but I don't know how to fix it
const LoggedInNavigator = () => (
<LoggedInStack.Navigator>
<LoggedInStack.Screen name="Home" component={Home} options={homeOptions} />
</LoggedInStack.Navigator>
);
const App: React.FC<IProps> = (props: IProps) => {
const { theme } = props;
const isLoggedIn = useSelector(
(state: IState) => state.loginReducer.isLoggedIn,
);
return (
<NavigationContainer ref={navigationRef} theme={theme}>
<StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />
<Stack.Navigator screenOptions={{ headerShown: false }}>
{isLoggedIn ? (
<Stack.Screen
name="AppHome"
component={LoggedInNavigator}
// options={homeOptions}
options={{ headerShown: false }}
/>
) : (
<Stack.Screen
name="Login"
component={AuthNavigator}
options={{
// When logging out, a pop animation feels intuitive
// You can remove this if you want the default 'push' animation
animationTypeForReplace: isLoggedIn ? 'push' : 'pop',
headerRight: () => <ThemeController />,
}}
/>
)}
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
thanks for your help
Related
I have made a Main Navigator in which I have made 2 sub navigators:
This is my MainNavigator:
import React, {useEffect, useState} from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import storage from '../services/storage';
import {useDispatch, useSelector} from 'react-redux';
import {setUser} from '../store/reducers/auth';
import AuthNavigation from './AuthNavigation';
import MainNavigation from './MainNavigation';
const {Navigator, Screen} = createStackNavigator();
const StackNavigation = () => {
const [isLogged, setIsLogged] = useState(false);
const dispatch = useDispatch();
const loadUser = async () => {
try {
const data = await storage.get('USER');
if (data !== null && data !== '') {
console.log('Hello user', data);
dispatch(setUser(data));
setIsLogged(true);
}
} catch (error) {
console.log('APP JS ERROR', error.message);
}
};
useEffect(() => {
loadUser();
}, []);
return (
<Navigator screenOptions={{headerShown: false}}>
{isLogged ? (
<Screen name="MainNavigation" component={MainNavigation} />
) : (
<Screen name="AuthNavigation" component={AuthNavigation} />
)}
</Navigator>
);
};
export default StackNavigation;
These are my sub navigators:
AuthNavigation:
import React from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import SignUpScreen from '../screens/AccountScreens/SignUpScreen';
import LoginScreen from '../screens/AccountScreens/LoginScreen';
import ForgotPassword from '../screens/AccountScreens/ForgotPassword';
import OTPVerification from '../screens/AccountScreens/OTPVerification';
import SetNewPassword from '../screens/AccountScreens/SetNewPassword';
const {Navigator, Screen} = createStackNavigator();
const AuthNavigation = () => {
const verticalAnimation = {
gestureDirection: 'vertical',
cardStyleInterpolator: ({current, layouts}) => {
return {
cardStyle: {
transform: [
{
translateY: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.height, 0],
}),
},
],
},
};
},
};
return (
<Navigator
initialRouteName={'Login'}
screenOptions={{headerShown: false, animationEnabled: true}}>
<Screen
name="SignUp"
component={SignUpScreen}
options={verticalAnimation}
/>
<Screen name="Login" component={LoginScreen} />
<Screen name="ForgotPassword" component={ForgotPassword} />
<Screen name="OTPVerification" component={OTPVerification} />
<Screen name="SetNewPassword" component={SetNewPassword} />
</Navigator>
);
};
export default AuthNavigation;
MainNavigation:
import React, {useState} from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import MyTabNavigation from './MyTabNavigation';
import GymDetailScreen from '../screens/InnerScreens/GymDetailScreen';
import AccountOverview from '../screens/DrawerScreens/AccountOverview';
import SubscriptionDetails from '../screens/DrawerScreens/SubscriptionDetails';
import BillingDetails from '../screens/DrawerScreens/BillingDetails';
import ChangePassword from '../screens/DrawerScreens/ChangePassword';
import BuySubscribtion from '../screens/InnerScreens/BuySubscribtion';
import ScanScreen from '../screens/InnerScreens/ScanScreen';
import PaymentSummary from '../screens/InnerScreens/PaymentSummary';
import PaymentMethod from '../screens/InnerScreens/PaymentMethod';
const {Navigator, Screen} = createStackNavigator();
const MainNavigation = () => {
const horizontalAnimation = {
cardStyleInterpolator: ({current, layouts}) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
},
};
},
};
return (
<Navigator
initialRouteName={'MyTabNavigation'}
screenOptions={{headerShown: false, animationEnabled: true}}>
<Screen name="GymDetailScreen" component={GymDetailScreen} />
<Screen
name="MyTabNavigation"
component={MyTabNavigation}
options={horizontalAnimation}
/>
<Screen name="AccountOverview" component={AccountOverview} />
<Screen name="SubscriptionDetails" component={SubscriptionDetails} />
<Screen name="BillingDetails" component={BillingDetails} />
<Screen name="ChangePassword" component={ChangePassword} />
<Screen name="BuySubscription" component={BuySubscribtion} />
<Screen name="ScanScreen" component={ScanScreen} />
<Screen name="PaymentSummary" component={PaymentSummary} />
<Screen name="PaymentMethod" component={PaymentMethod} />
</Navigator>
);
};
export default MainNavigation;
I want to go to MainNavigator on Login which is in AuthNavigator, Here is how I am doing this:
navigation.navigate('MainNavigation');
But its giving me this error:
The action 'NAVIGATE' with payload {"name":"MainNavigation"} was not handled by any navigator.
Do you have a screen named 'MainNavigation'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production.
Please help me with this coz I am trying this for the first time. Thanks in advance
You are using Authentication flows stated in React Navigation documents.
Please be noted that this approach using conditionally rendering screens which means screen is not rendered if condition is not fulfilled. So, application will cause error for navigation to non-existing screen.
What you need to do in order to jump to MainNavigation screen, is to set your
isLogged variable in MainNavigator to true.
navigation.navigate('MainNavigation');
Here you can not use navigate method because both are different Navigators
So you have to render your navigator on a Conditional base.
For ex:
you can save authTocken after Successful Login and in your main Route file
you can check write condition whether authTocken is null or not.
On that basis you can render your navigator Like,
{
authTocken != '' (
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={initialRoute}>
<Stack.Screen name={routes.Dasboard} component={Dashboard} />
<Stack.Screen name={routes.Setting} component={Setting} />
</Stack.Navigator>
)
:
<Stack.Navigator screenOptions={{ headerShown: false }}
initialRouteName={initialRoute}>
<Stack.Screen name={routes.Login} component={Login} />
<Stack.Screen name={routes.Registration} component=Registration} />
</Stack.Navigator>
}
import * as React from "react";
import { SafeAreaView, useWindowDimensions } from "react-native";
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from "#react-navigation/native";
import { RegistrationScreen } from './src/screens/Registration/RegistrationScreen';
import {LoginScreen} from './src/screens/Login/LoginScreen';
import {NewAnuncioScreen} from './src/screens/NewAnuncio/NewAnuncioScreen';
import { FeedScreen } from "./src/screens/Feed/FeedScreen";
import { FontAwesome, AntDesign, FontAwesome5, Entypo} from '#expo/vector-icons';
const Drawer = createDrawerNavigator()
const App = () => {
const dimensions = useWindowDimensions();
return (
<SafeAreaView style={{ flex: 1 }}>
<NavigationContainer>
<Drawer.Navigator initialRouteName="Feed" screenOptions={{
headerStyle: { backgroundColor: "#f2bc1b" , height: 55},
drawerType: dimensions.width >= 821 ? 'permanent' : 'front',
overlayColor: 'transparent',
drawerContent:{CustomDrawerContent}
}}>
<Drawer.Screen name="Feed" component={FeedScreen}/>
<Drawer.Screen name="Registration" component={RegistrationScreen} options={{ headerShown: false }} />
<Drawer.Screen name="Login" component={LoginScreen} options={{ headerShown: false }} />
{/* <Drawer.Screen name="Criar Anúncio" component={NewAnuncioScreen} /> */}
</Drawer.Navigator>
</NavigationContainer>
</SafeAreaView>
)
}
export default App;
**I want the header´s structures can vary like that:
enter image description here
enter image description here
But i don´t know how to make this.
If someone help me i thanks.**
You should pass the Drawer props to your custom drawer and retrieve the navigation index.
drawerContent = {(props) => <CustomDrawerContent {...props} />}
Then
const CustomDrawerContent = (props) => {
const [index, setIndex] = useState(0)
useEffect(() => {
setIndex(props.state.index);
}, [props])
...
Now according to this index you can change the appearance and the behavior your drawer content.
i've implemented theming support into my app with react-navigation, as you can see below.
i am using the system theme settings, and my app follows this rule
this is working great, but there's one thing left on my to-do list,
a option to toggle light/dark theme inside my app,
keep this selection, and store it into the user defaults or something like that..
i followed the official docs (https://reactnavigation.org/docs/themes/)
but they don't mention how to switch themes manually.
in my testing i always got a message that the theme prop is read only and cannot be changed manually.
so how to do that?
any help'd be greatly appreciated ;)
App.js
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { AppearanceProvider, useColorScheme, useTheme } from 'react-native-appearance';
const Stack = createStackNavigator();
// ------------------App-----------------------------
export default function App() {
const scheme = useColorScheme();
const MyDarkTheme = {
dark: true,
...},
const LightTheme = {
dark: false,
...}
return (
<AppearanceProvider>
<NavigationContainer theme={scheme === "dark" ? MyDarkTheme : LightTheme}>
<Stack.Navigator>
<Stack.Screen>
name="home"
...
<Stack.Screen
name="Settings"
component={Settings}
/>
</Stack.Navigator>
</NavigationContainer>
</AppearanceProvider>
);
}
in my Components:
import React, { useState, useEffect} from 'react';
import { Card } from 'react-native-elements';
import { useTheme} from '#react-navigation/native';
function CardOne(props) {
const { colors } = useTheme(); // works
const theme = useTheme();
return (
<Card containerStyle={{backgroundColor: colors.cardBackgroundColor, borderColor: colors.borderColor, borderWidth: 2, borderRadius: 5}}>
...
</Card>
);
}
export default CardOne;
i really someone can help me out ;)
You can use the Context and do something like below, basically maintain the theme in state at App.js and update value via context.
export const ThemeContext = React.createContext();
export default function App() {
const [theme, setTheme] = useState('Light');
const themeData = { theme, setTheme };
return (
<ThemeContext.Provider value={themeData}>
<NavigationContainer theme={theme == 'Light' ? DefaultTheme : DarkTheme}>
<Drawer.Navigator initialRouteName="Root">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Root" component={Root} />
</Drawer.Navigator>
</NavigationContainer>
</ThemeContext.Provider>
);
}
You can switch the theme from a screen like below
function ProfileScreen({ navigation }) {
const { setTheme, theme } = React.useContext(ThemeContext);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Profile Screen</Text>
<Button
title="Switch Theme"
onPress={() => setTheme(theme === 'Light' ? 'Dark' : 'Light')}
/>
</View>
);
}
Sample code
https://snack.expo.io/#guruparan/5b84d0
I am trying to make dynamic tab.screen.
my code is like this:
import React from 'react';
import { Text, View, TouchableOpacity, Modal } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Home from '../mainscreen/GeneralScreen';
import Core from '../mainscreen/CoreScreen';
import Docs from '../mainscreen/GeneralScreen';
import ESS from '../mainscreen/CoreScreen';
import General from '../mainscreen/GeneralScreen';
import HR from '../mainscreen/CoreScreen';
import Payroll from '../mainscreen/GeneralScreen';
import Server from '../mainscreen/CoreScreen';
const Tab = createBottomTabNavigator();
const tabcomponents = {
"Home" : Home,
"Core" : Core,
"Docs" : Docs,
"ESS" : ESS,
"General" : General,
"HR" : HR,
"Payroll" : Payroll,
"Server" : Server
};
class TabNavigator extends React.Component {
constructor() {
super();
this.state = {
dashboardtab:[],
}
this.tabnavigatorasync();
}
tabnavigatorasync = async () => {
try {
const dashboardtab = await AsyncStorage.getItem('dashboardtab');
const dashboardtabParse = JSON.parse(dashboardtab);
this.setState({dashboardtab: dashboardtabParse});
} catch (error) {
}
}
render(){
const tabnavigatorRender = this.state.dashboardtab.map((item, index) => {
const tabcomponentsrender = tabcomponents[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={tabcomponentsrender} key={index}/>
});
return(
<Tab.Navigator>
{tabnavigatorRender}
</Tab.Navigator>
)
}
}
export default TabNavigator;
the result appears an error like this:
Error: Couldn't find any screens for the navigator. Have you defined any screens as its children?
is there something wrong with the code i made?
As the error states you are not having any screens inside the TabNavigator.
When the component is mounted the array is empty and the data is loaded later.
So you can fix this like below
render(){
const tabnavigatorRender = this.state.dashboardtab.map((item, index) => {
const tabcomponentsrender = tabcomponents[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={tabcomponentsrender} key={index}/>
});
// Add this to return null or you can also show <ActivityIndicator/>
if(this.state.dashboardtab.length===0)
return null;
return(
<Tab.Navigator>
{tabnavigatorRender}
</Tab.Navigator>
)
}
Also call the tabnavigatorasync from componentDidMount instead of calling it from the constructor.
componentDidMount(){
this.tabnavigatorasync();
}
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerShown: false,
tabBarActiveTintColor: '#A968EE',
tabBarStyle: {
position: 'absolute',
backgroundColor: 'black',
},
}}>
{data?.map((val, index) => (
<Tab.Screen
key={index}
name={val?.Name}
options={{
tabBarLabel: val?.Name,
tabBarIcon: () => (
<Image
style={{
height: 20,
width: 20,
}}
source={val?.ImageSource}
/>
),
}}
component={val?.Component}
/>
))}
</Tab.Navigator>
</NavigationContainer>
This is App.js using react-navigation. There are two screens on it called HomeScreen and AddScreen.
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import HomeScreen from './src/HomeScreen';
import AddScreen from './src/AddScreen';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Add" component={AddScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
And This is home Screen. There is a 'items' in 'useState. It is gived through Add by navigation as props.
import * as React from 'react';
import PropTypes from 'prop-types';
import { View, Text, Button } from 'react-native';
function HomeScreen({ navigation, route }) {
const [items, setItems] = React.useState([]);
React.useEffect(() => {
if (route.params?.items) {
// Post updated, do something with `route.params.post`
// For example, send the post to the server
console.log('saved');
}
}, [route.params?.items]);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title="Create post"
onPress={() => navigation.navigate('Add', { items, setItems })}
/>
<View>
{items.map((item, i) => {
return (
<View>
<Text>{item.itemName}</Text>
<Text>{item.itemPrice}</Text>
</View>
);
})}
</View>
</View>
);
}
HomeScreen.propTypes = {
navigation: PropTypes.object.isRequired,
};
export default HomeScreen;
And AddScreen receives 'items' as route.params.
And it use 'setItems' to push his own data in it.
After adding, navigation return to HomeScreen with items that is added with new item.
import * as React from 'react';
import PropTypes from 'prop-types';
import { View, Text, Button, TextInput } from 'react-native';
function AddScreen({ route, navigation }) {
const { items, setItems } = route.params;
const [itemName, setItemName] = React.useState('');
const [itemPrice, setItemPrice] = React.useState('0');
const addItem = () => {
setItems([...items, { itemName, itemPrice }]);
setItemName('');
setItemPrice('0');
};
return (
<View>
<TextInput
multiline
placeholder="What's on your mind?"
value={itemName}
onChangeText={setItemName}
/>
<TextInput
multiline
placeholder="What's on your mind?"
value={itemPrice}
onChangeText={setItemPrice}
/>
<Button
title="Done"
onPress={() => {
addItem();
// Pass params back to home screen
navigation.navigate('Home', items);
}}
/>
</View>
);
}
AddScreen.propTypes = {
navigation: PropTypes.object.isRequired,
route: PropTypes.object.isRequired,
};
export default AddScreen;
It works well on my purpose.
But I'm not sure whether this way is correct or not using react hooks to give and receive data from parent to child.
Could you modify my code ?
You should consider using React Context API https://uk.reactjs.org/docs/context.html. Its dedicated to sharing the common state (items in your case). Here is an example:
You should create a common context for items:
ItemsState.js
import React, { useState, useContext } from 'react';
const ItemsContext = React.createContext([]);
export const ItemsProvider = ({ children }) => {
return (
<ItemsContext.Provider value={useState([])}>
{children}
</ItemsContext.Provider>
);
}
export const useItems = () => useContext(ItemsContext);
Then share the context between screens with provider in App.js like this
import {ItemsProvider} from 'ItemsState';
function App() {
return (
<ItemsProvider> // share the items between both screens
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Add" component={AddScreen} />
</Stack.Navigator>
</NavigationContainer>
</ItemsProvider>
);
}
Then use items context in each screen like this AddScreen.js
import {useItems} from './ItemsState';
function AddScreen({ route, navigation }) {
const [items, setItems] = useItems(); // <- using items context as global useState
const [itemName, setItemName] = React.useState('');
const [itemPrice, setItemPrice] = React.useState('0');
const addItem = () => {
setItems([...items, { itemName, itemPrice }]);
setItemName('');
setItemPrice('0');
};
return (
<View>
<TextInput
multiline
placeholder="What's on your mind?"
value={itemName}
onChangeText={setItemName}
/>
<TextInput
multiline
placeholder="What's on your mind?"
value={itemPrice}
onChangeText={setItemPrice}
/>
<Button
title="Done"
onPress={() => {
addItem();
// Pass params back to home screen
navigation.navigate('Home', items);
}}
/>
</View>
);
}
You can also use useReducer hook and make more Redux-like. Check out this article
https://medium.com/simply/state-management-with-react-hooks-and-context-api-at-10-lines-of-code-baf6be8302c
in order to share data between components you can use Context API or Redux, passing full objects through navigation routes is an anti-pattern, you can find more information in the docs
https://reactnavigation.org/docs/params/#what-should-be-in-params