I'm new to react native and I'm having this problem with Auth Screen navigation and Redux Store.
So when I press "Start" it doesn't navigate to ClassesListScreen but remain in the LoginScreen. Although when I console log it print out that the "isLoggedIn" property in Store is true but it still doesn't re-render. Why is that happening? Thank you very much.
Here's my StackNavigation.js:
import React from 'react'
import { createStackNavigator } from '#react-navigation/stack';
import StudentsTabNavInit from './StudentsTabNavigation';
import LoginScreen from '../components/login/LoginScreen';
import DetailsScreen from '../components/details/DetailsScreen';
import store from '../store/store'
import AddScreen from '../components/add/AddScreen';
import ClassesListScreen from '../components/classes/ClassesListScreen';
const StackNav = createStackNavigator();
import { connect } from 'react-redux'
function StackNavInit(){
return (
<StackNav.Navigator
initialRouteName="Login"
>
{store.getState().isLoggedIn? (
<>
<StackNav.Screen
name="Classes"
component={ClassesListScreen}
options={{title: "Your Classes"}}
/>
<StackNav.Screen
name="StudentsTabNav"
component={StudentsTabNavInit}
options ={{title: "Students"}}
/>
<StackNav.Screen
name="Details"
component={DetailsScreen}
options={{ title: "Details" }}
/>
<StackNav.Screen
name="Add"
component={AddScreen}
options={{title: "Add Course"}}
/>
</>
) : (
<>
<StackNav.Screen
name="Login"
component={LoginScreen}
options={{headerShown: false}}
/>
</>
)
}
</StackNav.Navigator>
)
}
export default StackNavInit
My LoginScreen.js:
import React from 'react'
import { render } from 'react-dom'
import { Text, View, Button} from 'react-native'
import store from '../../store/store'
import {login} from '../../store/actions'
class LoginScreen extends React.Component{
constructor(props){
super(props)
}
logIn = ()=> {
//console.log(store.getState().isLoggedIn)
store.dispatch(login());
//this.props.navigation.navigate("Classes")
};
render(){
return (
<View>
<Text> Welcome to Courses Managing Application</Text>
<Text> Press Start </Text>
<Button title="Start" onPress={this.logIn}></Button>
<Text> {store.getState().isLoggedIn} </Text>
</View>
)
}
}
export default LoginScreen
for further information, here are things in my store folder (But I don't think it's related):
actions.js:
export const UPDATE_INFOR = 'UPDATE_INFOR'
export const GET_DATA = 'GET_DATA'
export const ADD_EMPLOYEE = 'ADD_EMPLOYEE'
export const DATA_REQ_SENT = 'DATA_REQ_SENT'
export const DATA_REQ_SUCCEED = 'DATA_REQ_SUCCEED'
export const DATA_REQ_FAILED = 'DATA_REQ_FAILED'
export const LOG_IN = 'LOG_IN'
export const updateInfor = (infor) => ({type: UPDATE_INFOR, payload: infor})
export const addEmployee = (infor) => ({type: ADD_EMPLOYEE, payload: infor})
export const login = () => ({type: LOG_IN})
reducers.js:
import defaultState from './state'
function reducer(state = defaultState, action) {
switch (action.type) {
case 'LOG_IN':
return {...state, isLoggedIn: true}
default:
return state
}
}
export default reducer
state.js:
export default defaultState = {
isLoggedIn: false,
url: 'https://randomuser.me/api/',
results: 100,
nat:'us',
list_inc:'name,gender,dob,phone,picture,id',
}
store.js:
import { createStore, applyMiddleware } from 'redux'
import reducer from './reducers'
import thunk from 'redux-thunk'
export default store = createStore(reducer, applyMiddleware(thunk))
I figured out a way my self.
First wrap the App with the <Provider></Provider>
export default function App() {
return (
<Provider store ={store}>
<NavigationContainer>
<StackNavInit/>
</NavigationContainer>
</Provider>
);
}
then in StackNav use a Selector to connect a property isLoggedin to the store property with the same name, eg: const isLoggedIn = useSelector(state => state.isLoggedIn)
then the Navigator gonna look like:
function StackNavInit(){
const isLoggedIn = useSelector(state => state.isLoggedIn)
return (
<StackNav.Navigator
initialRouteName="Login"
>
{isLoggedIn? (
<>
<StackNav.Screen
name="Classes"
component={ClassesListScreen}
options={{title: "Your Classes"}}
/>
<StackNav.Screen
name="StudentsTabNav"
component={StudentsTabNavInit}
options ={{title: "Students"}}
/>
<StackNav.Screen
name="Details"
component={DetailsScreen}
options={{ title: "Details" }}
/>
<StackNav.Screen
name="Add"
component={AddScreen}
options={{title: "Add Course"}}
/>
</>
) : (
<>
<StackNav.Screen
name="Login"
component={LoginScreen}
options={{headerShown: false}}
/>
</>
)
}
</StackNav.Navigator>
)
}
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>
}
I couldn't find any solution in any other questions similar to mine so I decided to ask you for help.
I have tests to my <Login/> page:
import React from 'react';
import { render } from '../../__test__/utils';
import Login from '.';
test('Renders the Login page', () => {
const { getByTestId } = render(<Login />);
expect(getByTestId('login-page')).toBeInTheDocument(); //WORKS
});
test('Renders disabled submit button by default', () => {
const { getByTestId } = render(<Login />);
expect(getByTestId('login-button')).toHaveAttribute('disabled'); // ERROR HERE
});
test('should match base snapshot', () => {
const { asFragment } = render(<Login />);
expect(asFragment()).toMatchSnapshot(); //WORKS
});
I am using the 'utils.js' in my tests because I want to wrap every testing component in the providers I am using. Here is how the utils file looks like:
import React, { useState } from 'react';
import { render as rtlRender } from '#testing-library/react';
import '#testing-library/jest-dom/extend-expect';
import { renderRoutes } from 'react-router-config';
import { Router } from 'react-router-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import { ThemeProvider } from '#material-ui/styles';
import { createMemoryHistory } from 'history';
import StylesProvider from '../components/StylesProvider';
import routes from '../routes';
import reducer from '../reducers';
import { theme } from '../theme';
const history = createMemoryHistory();
const render = (
ui,
{ initialState, store = createStore(reducer, initialState), ...renderOptions } = {}
) => {
const Wrapper = ({ children }) => {
const [direction] = useState('ltr');
return (
<Provider store={store}>
<ThemeProvider theme={theme}>
<StylesProvider direction={direction}>
<Router history={history}>
{children}
{renderRoutes(routes)}
</Router>
</StylesProvider>
</ThemeProvider>
</Provider>
);
};
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
};
export * from '#testing-library/react';
export { render };
Unfortunately, I cannot show the whole app code so I'll make it short. The <Login /> component looks like below:
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import {
Card, CardContent, CardHeader, Typography, Divider, Grid
} from '#material-ui/core';
import Page from 'src/components/Page';
import LoginForm from './LoginForm';
const Login = () => (
<Page className={classes.root} title="Login" dataTestId="login-page">
//here the component body//
<LoginForm
afterLoginRoute="/overview"
loginEndpoint="login"
userRole={userRoles.USER}
className={classes.loginForm}
/>
</Page>
)
In the <LoginForm /> component I have a Material-UI button with data-testid="login-button"
LoginForm:
return (
<div>
<Snackbar open={loginStatus.error} autoHideDuration={6000}>
<Alert severity="error">
Se ha producido un error, comprueba los credenciales y prueba de nuevo.
</Alert>
</Snackbar>
<form {...rest} className={clsx(classes.root, className)} onSubmit={handleSubmit}>
<div className={classes.fields}>
<TextField
className={classes.input}
error={hasError('email')}
fullWidth
helperText={hasError('email') ? formState.errors.email[0] : null}
label="Email"
name="email"
onChange={handleChange}
value={formState.values.email || ''}
variant="outlined"
/>
<TextField
className={classes.input}
error={hasError('password')}
fullWidth
helperText={hasError('password') ? formState.errors.password[0] : null}
label="ContraseƱa"
name="password"
onChange={handleChange}
type="password"
value={formState.values.password || ''}
variant="outlined"
/>
</div>
<Button
className={classes.submitButton}
color="secondary"
disabled={!formState.isValid}
size="large"
type="submit"
variant="contained"
data-testid="login-button"
>
Entrar
</Button>
</form>
</div>
);
Current test result I have is TestingLibraryElementError: Found multiple elements by: [data-testid="login-button"]. I am out of ideas right now. Could this happen because I try to pass the data-testid prop to the <Button /> which is imported from Material-UI library? Then, shouldn't I see other error message? Please help.
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!
I am building an app in which several of the screens have dynamically rendered cards which are mapped to an array of objects called ENTRIES. Each one of these cards can be pressed to navigate to a corresponding screen, however I cannot seem to get the navigation to work.
I keep getting the following error Error: You need to specify name or key when calling navigate with an object as the argument.
I am passing is the screen value from ENTRIES into my this.props.navigation.navigate, which should direct to that screen when the <TouchableOpacity> is pressed.
Here is an an example of the code below:
App.js File
import React from 'react;
import {createMaterialBottomTabNavigator} from '#react-navigation/material-bottom-tabs';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Home from './src/screens/Home';
import SettingsScreen from './src/screens/SettingsScreen';
import PrivacyScreen from './src/screens/PrivacyScreen';
import NotificationsScreen from './src/screens/NotificationsScreen';
import SoundsScreen from './src/screens/SoundsScreen';
import ThemeScreen from './src/screens/ThemeScreen';
const PrivacyStack = createStackNavigator();
const SettingsStack = createStackNavigator();
const AuthStack = createStackNavigator();
const MainStack = createStackNavigator();
const Tabs = createMaterialBottomTabNavigator();
const TabNavigator = () => {
return (
<Tabs.Navigator
initialRouteName="Home"
<Tabs.Screen
name="Home"
component={HomeStack}
/>
Tabs.Screen
name="Settings"
component={SettingsStack}
children={this.SettingsStack}
</Tabs.Navigator>
)
}
const AuthStack = () => (
<AuthStack.Navigator>
<AuthStack.Screen
name="Auth"
component={Auth}
/>
</AuthStack.Navigator>
);
const SettingsStackScreen = () => (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="Settings"
component={Settings}
/>
<SettingsStack.Screen
name="Privacy"
component={PrivacyStack}
/>
<SettingsStack.Screen
name="Theme"
component={ThemeScreen}
/>
<SettingsStack.Screen
name="Notifications"
component={NotificationsScreen}
/>
<SettingsStack.Screen
name="Sound"
component={SoundsScreen}
/>
</SettingsStack.Navigator>
);
const PrivacyStack = () => (
<PrivacyStack.Navigator>
<PrivacyStack.Screen
name="Privacy"
component={PrivacyScreen}
/>
<PrivacyStack.Screen
name="Notifications"
component={NotificationsScreen}
/>
</PrivacyStack.Navigator>
);
const App = () => {
return (
<NavigationContainer ref={navigationRef}>
<MainStack.Navigator>
<MainStack.Screen name="Tabs" component={TabNavigator} />
<MainStack.Screen
name="Auth"
component={AuthStack}
options={{gestureEnabled: false}}
/>
</MainStack.Navigator>
</NavigationContainer>
)
}
Settings.js File
import React, {Component} from 'react';
import {TouchableOpacity, ScrollView} from 'react-native;
export default class Settings extends Component {
render(screen, index) {
return (
<ScrollView>
{ENTRIES.map((entry, index) => (
<TouchableOpacity
key={entry.index}
onPress={() => this.props.navigation.navigate(screen)}>
</TouchableOpacity>
))}
</ScrollView>
)
}
export default Settings
entries.js File
import React from 'react';
export const ENTRIES = [
{
name: "Theme",
screen: "ThemeScreen",
},
{
name: "Sounds",
screen: "SoundsScreen",
},
{
name: "Notifications",
screen: "NotificationsScreen",
},
]
The render function does not take any arguments. And the screen in your array should be accessed by using the entry.screen
render() {
return (
<ScrollView>
{ENTRIES.map((entry, index) => (
<TouchableOpacity
key={entry.name}
onPress={() => this.props.navigation.navigate(entry.screen)}>
</TouchableOpacity>
))}
</ScrollView>
)
}
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