I am trying to get AsyncStorage on B screen from A screen. On A screen i am saving them and getting them on B, but problem seems to be like that when i navigate to screen b i only get the values when i refresh B screen. It works fine in Expo but in emulator it doesn't.
Please have a look at my code :
import React from 'react';
import { Button, View, Text, TextInput, TouchableOpacity } from 'react-native';
import { styles } from './styles';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import AsyncStorage from '#react-native-async-storage/async-storage';
function HomeScreen({ navigation }) {
const [email, setEmail] = React.useState('');
const redirect = (emailAddress) => {
storeData(emailAddress);
navigation.navigate('Details');
};
const storeData = async (emailAddress) => {
try {
await AsyncStorage.setItem('email', emailAddress);
} catch (e) {
console.log('Error Saving Data', e);
}
};
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TextInput
style={styles.input}
value={email}
onChangeText={(email) => setEmail(email)}
placeholder="Enter email address here"
placeholderTextColor="rgba(62, 88, 112, 0.5)"
keyboardType="email-address"
underlineColorAndroid={'transparent'}
autoCapitalize="none"
/>
<TouchableOpacity style={{ margin: 20 }} onPress={() => redirect(email)}>
<Text style={styles.registerText}> Register</Text>
</TouchableOpacity>
</View>
);
}
function DetailsScreen() {
const [text, setText] = React.useState('');
React.useEffect(() => {
const getEmail = async () => {
try {
const email = await AsyncStorage.getItem('email');
if (email !== null) {
setText(email);
}
} catch (e) {
console.log(' error : ' + e);
}
};
getEmail();
}, []);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen : {text}</Text>
</View>
);
}
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Please help me solve this.
Solution:
To make the details page get the value correctly you need to set the value and wait for it to set in your home page before navigating to details screen so you can get it in the next screen.
const redirect = async (emailAddress) => {
await storeData(emailAddress);
navigation.navigate('Details');
};
Better Solution:
First of all, you don't need to store email in your asyncStorage and retrieve it in the details you can pass it as a route param like this:
navigation.navigate('Details', {email});
and in your details screen:
function DetailsScreen({route}) {
const {email} = route.parmas;
...
}
Related
Well, I made a presentation onboarding screen, as soon as the user opens the app, the screen is shown, but I want this to be saved using AsyncStorage, if I close and open the app, the onboarding screen is not shown, but the screen of login.
I did all the code but nothing happens and the screen is displayed every time I close and open the app, I don't know what I'm doing wrong, code below.
App.js
import React, { useEffect, useState } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {AuthProvider} from './src/providers/Auth';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { OnBoarding } from './src/screen/OnBoarding';
import { SignIn } from './src/screen/SignIn';
import { HomeScreen } from './src/screen/HomeScreen';
import { ActivityIndicator } from 'react-native';
const Stack = createNativeStackNavigator();
const Loading = () => {
return (
<View>
<ActivityIndicator size="large" />
</View>
);
}
export function App(){
const [loading, setLoading] = useState(true);
const [viewedOnboarding, setViewedOnboarding] = useState(false);
useEffect(() => {
checkOnBoarding();
}, [])
const checkOnBoarding = async () => {
try{
const value = await AsyncStorage.getItem('#viewedOnboarding');
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}
return (
<AuthProvider>
<NavigationContainer>
<Stack.Navigator
initialRouteName={loading ? <Loading /> : viewedOnboarding ? <HomeScreen /> : <OnBoarding />}
screenOptions={
{headerShown: false}
}
>
<Stack.Screen
name="SignIn"
component={SignIn}
/>
</Stack.Navigator>
</NavigationContainer>
</AuthProvider>
);
}
Onboarding.js
import React, { useEffect, useState } from 'react';
import {Text, View, StyleSheet, Image, TouchableOpacity, Button} from 'react-native';
import AppIntroSlider from 'react-native-app-intro-slider';
import Icon from 'react-native-vector-icons/FontAwesome';
import AsyncStorage from '#react-native-async-storage/async-storage';
const slides = [
{
key: 1,
title: 'Only Books Can Help You',
text: 'Books can help you to increase your knowledge and become more successfully.',
image: require('../../assets/imagem1.png'),
},
{
key: 2,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
{
key: 3,
title: 'Learn Smartly',
text: 'It’s 2022 and it’s time to learn every quickly and smartly. All books are storage in cloud and you can access all of them from your laptop or PC.',
image: require('../../assets/imagem2.png'),
},
];
export function OnBoarding(){
function renderItem({item}){
return (
<View style={styles.container}>
<Image style={styles.image} source={item.image} />
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.content}>{item.text}</Text>
</View>
);
}
function _renderPrevButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-left"
color="#000"
size={40}
/>
</View>
);
};
function _renderNextButton() {
return (
<View style={styles.buttonCircle}>
<Icon
name="angle-right"
color="#000"
size={40}
/>
</View>
);
};
const onPressFinish = async () => {
try{
await AsyncStorage.setItem('#viewedOnboarding', 'true')
navigation.navigate('SignIn');
}catch(err) {
console.log("Error #setitem ", err);
}
};
const renderDoneButton = () => {
return (
<TouchableOpacity onPress={onPressFinish}>
<Text style={{color: "#000"}}>Done</Text>
</TouchableOpacity>
);
};
return (
<AppIntroSlider
data={slides}
renderItem={renderItem}
keyExtractor={item => item.key}
renderPrevButton={_renderPrevButton}
renderNextButton={_renderNextButton}
renderDoneButton={renderDoneButton}
showPrevButton
showDoneButton
dotStyle={{backgroundColor: '#9D9D9D'}}
activeDotStyle={{backgroundColor: '#DE7773'}}
/>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: "#fff"
},
title: {
color: "#292B38",
fontSize: 24,
fontWeight: 'bold',
marginTop: 50
},
content: {
color: "#4D506C",
textAlign: 'center',
padding: 25,
lineHeight: 18
},
image: {
width: 300,
height: 300,
},
button: {
color: "#000",
backgroundColor: "transparent"
}
})
Probably because of the async nature of AsyncStore when you open your app first you init viewedOnboarding with false, then start reading storage and then you check is onboarding done or not. And of course because storage is async first time you check in Navigation condition you always get
I have modified your checkOnBoarding function.
Here is the code:
const checkOnBoarding = () => {
try{
AsyncStorage.getItem('#viewedOnboarding').then(value => {
if(value !== null){
setViewedOnboarding(true);
}
console.log(value);
})
}catch(err) {
console.log('Error #checkOnboarding: ', err);
}finally {
setLoading(false)
}
}
I am passing a value from screen one
navigation.setParams({imageData:images[0]})
navigation.navigate('screenTwo')
OR
navigation.navigate('screenTwo',{imageData:images[0]})
But I am not able to access the value in screenTwo using
const screenTwo = ({navigation}) =>{
const itemId = navigation.getParam('imageData', 'NO-ID');
}
I am getting error
ERROR TypeError: navigation.getParam is not a function. (In 'navigation.getParam('imageData', 'NO-ID')', 'navigation.getParam' is undefined)
Any help would be greatly appreciated
"#react-navigation/drawer": "^5.12.5",
"#react-navigation/native": "^5.9.4",
"#react-navigation/stack": "^5.14.5",
I tried below code in the same page
navigation.setParams({imageData:images[0]})
console.log(navigation.params);
output : LOG undefined
No more getParam
Previously we could also use navigation.getParam('someParam', 'defaultValue') to get a param value. It addressed 2 things:
Guard against params being undefined in some cases
Provide a default value if the params.someParam was undefined or null
Official documentation: https://reactnavigation.org/docs/upgrading-from-4.x/#the-navigation-prop
Your solution
const screenTwo = ({navigation, route}) => {
const itemId = route.params?.imageData ?? 'NO-ID';
}
Online example: https://snack.expo.io/#vasylnahuliak/stackoverflow-68240142
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const HomeScreen = ({ navigation }) => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Home screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details', { text: 'Hello, I am text from params' })}
/>
</View>
);
};
const DetailsScreen = ({route}) => {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Details screen</Text>
<Text style={{padding: 40}}>{JSON.stringify(route.params)}</Text>
</View>
);
};
const RootStack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen name="HomeScreen" component={HomeScreen} />
<RootStack.Screen name="Details" component={DetailsScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
}
solution for me worked in two-part:
problem of passing the param from first page to second page
solution: react-native navigation 5 undefined params
navigation.navigate('MainNavigationName', {
screen: 'ScreenNameToNavigate',
params: { propertyKey: propertyValue },
})
reading values from route
const itemId = route.params?.imageData ?? 'NO-ID';
thanks a lot Vasyl and Bora
Im trying to render react native component as function return, without sucesses, here is the code:
// In App.js in a new project
import * as React from 'react';
import { View, Text, TouchableOpacity, Linking, Alert } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import * as Google from 'expo-auth-session/providers/google';
import * as WebBrowser from 'expo-web-browser';
import { FacebookSocialButton, GoogleSocialButton } from "react-native-social-buttons";
import { AuthRequest, useAuthRequest } from 'expo-auth-session';
class MainClass extends React.Component {
constructor(props) {
super(props);
this.state = {
}
WebBrowser.maybeCompleteAuthSession();
Stack = createStackNavigator();
() => App(() => renderRoutes());
}
setResponse = (response) => {
this.setState({response:response}).then(Alert.alert(this.state.response));
}
LoginGoogle = () => {
const [request, response, promptAsync] = Google.useAuthRequest({
androidClientId: 'xxxxxxx.apps.googleusercontent.com',
expoClientId: 'xxxxxxxx.apps.googleusercontent.com'
});
React.useEffect(() => {
if (response?.type === 'success') {
const { authentication } = response;
}
}, [response]);
return (
<GoogleSocialButton disabled={!request} onPress={() => {promptAsync().then(() => {const [request, response, promptAsync] = useAuthRequest({}, {setResponse(response){}})})}} />
)
}
LoginScreen = (LoginGoogle) => {
const nav = this.props.navigation;
return (
<View style={{ flex: 1, backgroundColor: "#a2eff5"}}>
<View style={{flex: 0.15}}></View>
<View style={{flex: 0.1, backgroundColor: "white", borderRadius: 100/2, alignItems: "center", justifyContent: "center"}}>
<Text style={{color: "black"}}>Please, Login using buttons below!</Text>
</View>
<View style={{flex: 0.2}}></View>
<View style={{alignItems:"center", justifyContent: "center"}}>
{LoginGoogle()}
</View>
<View style={{flex: 0.05}}></View>
<View style={{alignItems:"center", justifyContent: "center"}}>
<FacebookSocialButton onPress={() => {}} />
</View>
</View>
);
}
MainScreen = () => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
renderRoutes = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName={"Login"}
screenOptions={{headerShown: false}}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Main" component={MainScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
}
App = (RenderComponent) => {
return (
{RenderComponent}
);
}
export default App;
And here is the error im getting:
Objects are not valid as React child (found: object with keys {RenderComponent}) If you meant to render a collection of children, use an array instead
Anyone know how this should be done?
The commenters are correct, you are trying to use a hook as a class method in a class component, it's not going to work. Additionally, hooks shouldn't return components. You can easily keep all your logic with a bit or rearranging, move the hook out of the class into a hook, then consume it in a functional component.
const useLoginGoogle = () => {
const [request, response, promptAsync] = Google.useAuthRequest({
androidClientId: 'xxxxxxx.apps.googleusercontent.com',
expoClientId: 'xxxxxxxx.apps.googleusercontent.com'
});
React.useEffect(() => {
if (response?.type === 'success') {
const { authentication } = response;
}
}, [response]);
return {promptAsync, disbaled: !request};
};
const LoginGoogle = () => {
const { promptAsync, disbaled } = useLoginGoogle();
return (
<GoogleSocialButton disabled={disbaled} onPress={() => { promptAsync().then(() => { const [request, response, promptAsync] = useAuthRequest({}, { setResponse(response) { } }) }) }} />
);
};
// Then wherever you want your button
<LoginGoogle />
I just added the loader that is loaded when the user sends the request to login by clicking the button with this code:
import React, { useState } from 'react';
import { Text, TextInput, SafeAreaView, StyleSheet, TouchableOpacity, Image, KeyboardAvoidingView, ActivityIndicator } from 'react-native';
import {AuthContext} from './utils';
// Creating Login Activity.
export function SignInScreen() {
const [isLoading, setLoading] = useState(false);
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const { signIn } = React.useContext(AuthContext);
return (
<KeyboardAvoidingView behavior={Platform.OS == 'ios' ? 'padding' : 'height'} style={styles.keyboard_login}>
<SafeAreaView style={styles.contenitore_login}>
{isLoading == true ? <ActivityIndicator size="large"/> : (
<>
<Image style={styles.logo} source={require('../assets/logo.png')} />
<TextInput
style={styles.campo_login}
placeholder="Email"
value={email}
onChangeText={setEmail}
autoCapitalize='none'
textContentType='emailAddress'
keyboardType='email-address'
/>
<TextInput
style={styles.campo_login}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
/>
<TouchableOpacity style={styles.cta_login} onPress={() => { setLoading(true); signIn({ email, password });}}>
<Text style={styles.testo_cta_login}>Accedi</Text>
</TouchableOpacity>
</>
)}
</SafeAreaView>
</KeyboardAvoidingView>
);
}
Everything works perfectly when the user enters the correct email and password, but when they enter the wrong credentials the loader keeps going.
}).then((response) => response.json()).then((responseJson) => {
// If server response message same as Data Matched
if(responseJson === 'Data Matched'){
dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' });
}else{
Alert.alert(responseJson);
}
}).catch((error) => {
console.error(error);
});
As you can see if the fetch URL says "Data Matched" then it is assigned SIGN_IN which then returns the user to the home of my app, but in the case where the logins don't match I don't know what to do to send the user back to the login page.
What is the navigation code that is used in this case? Thanks!
We can have variable let's say loading
const [loading, setLoading] = useState(false)
And as far I can see your Sigin function connects to db. So do something like
const authContext = React.useMemo(
() => ({
signIn: async data => {
setLoading(true);
// Some code here
setLoading(false);
}
})
)
And then show an Activity indicator or any component you want to show fetching something like this
import React from 'react';
import PropTypes from 'prop-types';
import {View, ActivityIndicator, Modal} from 'react-native';
import styles from './Styles/FullScreenLoaderStyles';
const FullScreenLoader = ({loading}) => (
<Modal transparent={true} animationType={'none'} visible={loading}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator color="#000" animating={loading} />
</View>
</View>
</Modal>
);
FullScreenLoader.propTypes = {
loading: PropTypes.bool,
};
export default FullScreenLoader;
I have updated the loading screen code. Now you can import this in your google signin screen like
import FullScreenLoader from './FullScreenLoader';
// some code
return(
<View>
<FullScreenLoader loading={isLoading} />
</View>
)
FullScreenLoaderStyles.js
import {StyleSheet} from 'react-native';
import colors from '../../Themes/Colors';
export default StyleSheet.create({
modalBackground: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#00000040',
},
activityIndicatorWrapper: {
backgroundColor: colors.grey,
height: 50,
width: 50,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
});
I want to remove the button back, but leave the header.
My component is as follows. I want to leave the title, and I don’t need the back button.
import React from 'react';
import { View } from 'react-native';
export const TrucksScreen = () => {
return (
<View>
....
</View>
);
});
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
};
How can I remove the button back?
Using headerLeft: null will be deprecated in future versions.
Instead use a function like so :
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: () => {
return null;
},
};
Cheers !
set headerLeft: null in the navigation Options. and this will remove the back button from the head as I did in the last line of code.
import React from 'react';
import { View } from 'react-native';
export const TrucksScreen = () => {
return (
<View>
....
</View>
);
});
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: null,
};
I hope it will help. Ask for doubts
According to the docs you can replace the header back button with whatever you want by passing options param in stack navigator . Do find the working example : expo-snack:
import * as React from 'react';
import { View, Text, Button, Image } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
function HomeScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Home Screen</Text>
</View>
);
}
function LogoTitle() {
return (
<Image
style={{ width: 50, height: 50 }}
source={require('#expo/snack-static/react-native-logo.png')}
/>
);
}
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerRight: () => (
<Button
onPress={() => alert('This is a button!')}
title="Info"
color="#00cc00"
/>
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Hopeit helps. feel free for doubts
import React from 'react';
import { View, TouchableOpacity, Image, Text } from 'react-native';
import PropTypes from 'prop-types';
import style from '../../../utils/style';
import images from '../../../images/images';
class Header extends React.Component {
constructor(props) {
super(props);
}
onRightIconPress = () => {
if (this.props.onRightIconPress) {
this.props.onRightIconPress();
}
};
render() {
const { title, navigation, showBackIcon, showRightIcon, rightIcon } = this.props;
return (
<View style={style.headerrowcontainer}>
{/* Back Button*/}
{showBackIcon ? (
<TouchableOpacity onPress={() => navigation.goBack()}>
<Image resizeMode="contain" source={images.iconback} style={style.backimage} />
</TouchableOpacity>
) : (
<View />
)}
{/* Title */}
<Text style={style.titleheader}>{title}</Text>
{/* Right Icon */}
{showRightIcon ? (
<Image name={rightIcon} style={style.rightIcon} onPress={this.onRightIconPress} />
) : (
<View />
)}
</View>
);
}
}
Header.defaultProps = {
title: '',
};
Header.propTypes = {
title: PropTypes.string,
};
export default Header;
Home: {
screen: HomeScreen,
navigationOptions: {
headerLeft: null,
}
}
try setting headerLeft: null
Doesn't work in RN6.
TrucksScreen.navigationOptions = {
headerTitle: 'Trucks Screen',
headerLeft: () => {
return null;
},
};