Navigate to another StackNavigtor screen on button press React Native - javascript

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>
}

Related

React Native, Drawer navigation not show, the app is closes

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

How can i modify the header structure of Drawer Navigator, taking into account that each screen will have a different header?

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.

How to successfully logout and navigate back to the main App screen in React-Native?

I need help figuring out the right way to logout and return to the Welcome screen.
Currently, the RootStack queries the redux store for a token, and uses that to determine which group of screens will be available to the user.
If a token exists we go into a TabNavigator called OrgTabs. And that seems to be taking removing the parent navigator. And when I try to logout, the screen I want to navigate to doesn't exist within the navigation object.
Please help! I'd really appreciate
Rootstack.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
//screens
import Welcome from '../screens/Welcome';
import SignIn from '../screens/SignIn';
import UserSignIn from '../screens/UserSignIn.js';
import StaffSignIn from '../screens/StaffSignIn';
import StaffSignUp from '../screens/StaffSignUp';
import OrgSignIn from '../screens/OrgSignIn';
import OrgSignUp from '../screens/OrgSignUp';
import OrgHomeScreen from '../screens/org/OrgHomeScreen';
import OrgTabs from '../components/molecules/OrgTabs';
import TestScreen from '../screens/org/TestScreen';
// selecetors and hooks
import { useSelector } from 'react-redux';
import { selectToken, selectRefreshToken } from '../features/user/userSlice';
const Stack = createStackNavigator();
const RootStack = () => {
const token = useSelector(selectToken)
const refreshToken = useSelector(selectRefreshToken)
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: true,
headerTitle:'',
headerTransparent: true
}}
>
{ token ?
(<Stack.Group>
<Stack.Screen name="OrgTabs" component={OrgTabs} />
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="TestScreen" component={TestScreen} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
</Stack.Group>
)
:
// auth screens
(<Stack.Group>
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
<Stack.Screen name="OrgTabs" component={OrgTabs} />
</Stack.Group>
)
}
</Stack.Navigator>
</NavigationContainer>
);
}
export default RootStack;
StaffSignIn.js
import React from "react";
import { Text, TextInput, ActivityIndicator } from "react-native";
import { StatusBar } from 'expo-status-bar';
import { Formik, Form } from "formik";
import { useDispatch, useSelector } from "react-redux";
import {
LightContainer, PadlessContainer, FlexHoriztal,
Header1, Header2, Header3, TextLight,
AppLogo, StlyedButton,
Pad_h_medium, Pad_h_small, Pad_w_small} from "../styles/styles";
import { TextLink, MsgBox } from "../components/atoms/Atoms";
import { StyldTextInput } from "../components/molecules/Molecules";
import { StyledFormArea } from "../components/organisms/Organisms";
import KeyboardAvoidingWrapper from "../components/organisms/KeyboardAvoidingWrapper";
import * as SecureStore from "expo-secure-store";
import axios from "axios";
import { setToken } from "../features/user/userSlice";
import {getToken} from "../features/user/User";
const logo_img = require("../assets/logo_red.png");
const chalk = require('chalk');
const StaffSignIn = ({navigation}) => {
[message, setMessage] = React.useState("");
[messageStatus, setMessageStatus] = React.useState("failed");
[hidePassword, setHidePassword] = React.useState(true);
const dispatch = useDispatch();
const handleSubmit = (values, setSubmitting) => {
const url = "https://dandle.dustinc.dev/signin/staff";
axios.post(url, values)
.then( (response) => {
const result = response.data;
const {success, user, token, refreshToken} = response.data;
if(success === true) {
setMessageStatus("success");
setMessage("sign in successful");
storeToken(token, refreshToken);
dispatch(setToken(token));
navigation.navigate("OrgTabs");
}
else if (success === false) {
setMessageStatus("failed");
setMessage("sign in failed");
}
setSubmitting(false);
})
.catch(err => {
setSubmitting(false);
// setMessage("Oops! Network error. Try again soon");
if (err.response.status === 401) {
setMessage("Invalid username or password");
}
else {
setMessage("Oops! Network error. Try again soon");
}
})
.finally(() => {
setSubmitting(false);
});
}
// function that will store a token and refresh token in react-native-keychain
async function storeToken (token, refreshToken) {
SecureStore.setItemAsync("token", token)
.then(() => {
SecureStore.setItemAsync("refreshToken", refreshToken)
.then(() => {
dispatch(setToken(token));
dispatch(setToken(refreshToken));
console.log("\x1b[32m successfully stored token and refresh-token\n");
console.log("\x1b[0m token: ", token,'\n');
console.log("refreshToken: ", refreshToken, '\n');
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
}
return (
<KeyboardAvoidingWrapper>
<LightContainer>
<StatusBar style="dark" />
<PadlessContainer>
<Pad_h_medium /><Pad_h_medium />
<Formik
initialValues={{
username: "",
password: "",
}}
onSubmit={(values, {setSubmitting}) => {
handleSubmit(values, setSubmitting);
}}
>
{
({handleChange, handleBlur, handleSubmit, isSubmitting, values}) => (
<StyledFormArea justify='center'>
<StyldTextInput
label="Username"
placeholder="johndoe"
onChangeText={handleChange("username")}
value={values.username}
/>
<StyldTextInput
label="Password"
placeholder="* * * * * * *"
secureTextEntry={hidePassword}
value={values.password}
onChangeText={handleChange('password')}
isPassword={true}
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<MsgBox type={messageStatus}>{message}</MsgBox>
{!isSubmitting && <StlyedButton onPress={handleSubmit} width='100%'>
<TextLight>Sign in</TextLight>
</StlyedButton>}<Pad_h_small />
{isSubmitting && <StlyedButton width='100%' disabled={true}>
<ActivityIndicator size="small" color="#fff" />
</StlyedButton>}<Pad_h_small />
<FlexHoriztal justify='center'>
<Header2>Don't have an account?</Header2>
<TextLink onPress={()=> navigation.navigate('StaffSignUp')}>
<Header2>Register</Header2>
</TextLink>
</FlexHoriztal>
</StyledFormArea>
)
}
</Formik>
</PadlessContainer>
</LightContainer>
</KeyboardAvoidingWrapper>
);
}
export default StaffSignIn;
OrgTabs.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {Octicons} from '#expo/vector-icons';
//screens
import OrgSettings from '../../screens/org/OrgSettings';
import OrgHomeScreen from '../../screens/org/OrgHomeScreen';
import OrgAnalytics from '../../screens/org/OrgAnalytics';
import OrgChat from '../../screens/org/OrgChat';
const Tab = createBottomTabNavigator();
export default function OrgTabs() {
return (
<NavigationContainer
independent={true}
>
<Tab.Navigator
screenOptions={{
tabBarShowLabel: false,
tabBarStyle: {
position: 'absolute',
backgroundColor: '#f38484',
borderTopRightRadius: 2,
borderTopLeftRadius: 2,
},
headerTransparent : true,
headerShown: false,
tabBarActiveBackgroundColor: '#d64547',
}}
>
<Tab.Screen
name="Settings"
component={OrgSettings}
options={{
tabBarIcon: () => {
return(
<Octicons name='gear' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Home"
component={OrgHomeScreen}
options={{
tabBarIcon: () => {
return(
<Octicons name='home' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Chat"
component={OrgChat}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='comment-discussion' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Analytics"
component={OrgAnalytics}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='graph' size={25} color='white'/>
)
}
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
So the solution I came up with, was to display the message "Logged out" and then set the token in redux to Null.
And finally, reload the app using the Updates package, which is part of Expo.
So when you logout, the app is reloaded and the check for a token is repeated.
Try this,I Hope it will work.
const logout=()=>{
console.log("logout");
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiRemove(keys)).then(() => navigation.reset({ index: 0, routes: [{ name: "Login" }], }));
}

Getting a modal to be behind tabNavigator using Modlize alwaysOpen

So I'm pretty new to programming and while making my first React Native project I got stuck trying to create a modal that's always open behind the bottom nav (The reason I want it that way is for it to be like a cart where whenever the user adds something it'll slightly nudge up to show something new was added), I did manage to get the modal behind the tab but now the bottom tab is unresponsive, does anyone have a fix for this?
my app.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import Header from './components/Header';
import RootStack from './components/RootStack';
const App = () => {
return (
<>
<Header />
<NavigationContainer>
<RootStack />
</NavigationContainer>
</>
);
};
export default App;
RootStack:
import React from 'react'
import { createStackNavigator } from '#react-navigation/stack'
import CarrinhoModal from '../CarrinhoModal'
import TabNavigator from '../TabNavigator';
const StackNavigation = createStackNavigator();
const RootStack = () => {
return (
<>
<StackNavigation.Navigator headerMode='none' >
<StackNavigation.Screen name='Tab' component={TabNavigator} />
<StackNavigation.Screen name='Carrinho' getComponent={CarrinhoModal} />
</StackNavigation.Navigator>
</>
)
}
export default RootStack;
TabNavigator:
import React from 'react';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Home from '../../pages/Home'
import Perfil from '../../pages/Perfil'
import CarrinhoModal from '../CarrinhoModal';
const Tab = createBottomTabNavigator();
const TabNavigator = ({ navigation }) => {
return (
<>
<Tab.Navigator
initialRouteName="Home"
tabBarOptions={{
activeTintColor: 'white',
inactiveTintColor: 'gray',
activeBackgroundColor: '#1E4B75',
inactiveBackgroundColor: '#1E4B75'
}}
>
{/* <Tab.Screen
name='Carrinho'
component={CarrinhoModal}
listeners={( navigation ) => ({
tabPress: e => {
e.preventDefault();
navigation.navigate(CarrinhoModal)
}
})}
/> */}
<Tab.Screen name='Home' component={Home} />
<Tab.Screen name='Perfil' component={Perfil} />
</Tab.Navigator>
<CarrinhoModal />
</>
)
}
export default TabNavigator;
CarrinhoModal:
import React, { useRef } from 'react'
import { Modalize } from 'react-native-modalize';
import Carrinho from '../../pages/Carrinho';
const CarrinhoModal = (props) => {
return (
<>
<Modalize
ref={props.modalRef}
alwaysOpen={75}
modalHeight={400}
handlePosition='inside'
>
<Carrinho />
</Modalize>
</>
)
}
export default CarrinhoModal;
As I said I'm pretty new in all this so if you could point me in the right direction it would be much apreciated!

How to use react hooks on react-native with react-navigation

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

Categories