I’m making an android app and recently added onBoarding screens, so I edited my app.js (thats contain the navigator) to make it perfect.
Before adding onBoarding Screens, I was able to use ‘this.props.navigation.navigate(‘Main’) to navigate from my login Page to my Main Page. Now, I can’t anymore. So I tried using ‘withNavigation’ from ‘react-navigation’ : I wrapped my Login class with this HOC but got this error:
Invariant Violation: withNavigation can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.
And I bet my component is wrapped in a navigator :/
Here’s my App.js :
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import React, { useState, useEffect } from 'react';
import {DEFAULT} from "./.app/public/themes/variables"
import AsyncStorage from '@react-native-async-storage/async-storage'; // -> necessaire pour le onboarding
import * as NavigationBar from 'expo-navigation-bar';
import * as StatusBar from 'expo-status-bar';
import Login from "./.app/public/auth/login.js" //devlogin
import BottomNav from "./.app/public/navigator/navbar.js"
import LoginWithoutFastCo from "./.app/public/auth/loginwofc.js"
import OnboardingScreen1 from './.app/public/components/onboarding/1.js';
import { NavigationContainer } from '@react-navigation/native';
const AppNavigator = createStackNavigator(
{
// Test: Test,
Login: Login,
Main: BottomNav,
Reload: LoginWithoutFastCo,
Onboarding1: OnboardingScreen1,
},
{
headerMode: "none",
initialRouteName: 'Onboarding1',
},
NavigationBar.setBackgroundColorAsync("black"),
NavigationBar.setButtonStyleAsync("light")
);
const AppContainer = createAppContainer(AppNavigator);
export default function App() {
const [showOnboarding, setShowOnboarding] = useState(true);
useEffect(() => {
// check if onboarding has been shown before
// you can use AsyncStorage or any other method to persist the state
const checkOnboardingShown = async () => {
const hasShownOnboarding = await AsyncStorage.getItem('hasShownOnboarding');
if (hasShownOnboarding) {
setShowOnboarding(false);
} else {
await AsyncStorage.setItem('hasShownOnboarding', 'true');
}
};
checkOnboardingShown();
}, []);
if (showOnboarding) {
return (
<NavigationContainer independent={true}>
<AppContainer />
</NavigationContainer>
);
} else {
return (
<NavigationContainer independent={true}>
<Login /> //HERE'S CALLED MY COMPONENT
</NavigationContainer> //AND HERE'S THE WRAP (I SUPPOSE)
);
}
}
And Here’s my login class component :
//import modules
import {imports} from '../../private/imports.js';
import React, { useState } from 'react';
import { ToastAndroid, Text, View, TextInput, TouchableOpacity, Image, KeyboardAvoidingView, Pressable, Keyboard } from "react-native";
import { Ionicons } from '@expo/vector-icons'
import * as NavigationBar from 'expo-navigation-bar';
import { encrypt, decrypt } from "../util/crypto";
import AsyncStorage from '@react-native-async-storage/async-storage';
import { withNavigation } from 'react-navigation';
import {defaultCSS} from "../stylesheets/_default/login"; //default theme
//import assets/utils
const logo = require('../assets/images/logoRond.png')
let dataReady = false;
class Login extends React.Component {
// Définition de la fonction errorMessage qui renvoie un message d'erreur sur android ou sur iOS
errorMessage = (err) => {
if (Platform.OS === 'android') { // Si l'OS est android
ToastAndroid.show(err, ToastAndroid.SHORT) // Affiche un message d'erreur sur android
} else if (Platform.OS === "ios") { // Sinon si l'OS est iOS
AlertIOS.alert(err); // Affiche une alerte sur iOS
} else {
alert(err); // Sinon affiche une alerte
}
}
state = {
keptName: "",
keptPassword: "",
disabledButton: false,
}
//verification des données
continue = async ({ navigation }) => {
this.setState({ disabledButton: true})
if ( this.state.name == "" || this.state.pwd == "" || this.state.name == undefined || this.state.pwd == undefined) {
if (await AsyncStorage.getItem("username") != "" || await AsyncStorage.getItem("username") != undefined || await AsyncStorage.getItem("password") != "" || await AsyncStorage.getItem("password")!= undefined) {
this.state.name = decrypt(await AsyncStorage.getItem("username"));
this.state.pwd = decrypt(await AsyncStorage.getItem("password"));
}
else return this.errorMessage("Merci de remplir tout les champs de connexion :)")
}
let userN = this.state.name;
userN = userN.slice(0, userN.indexOf('.'));
console.log('x1b[36m%sx1b[0m', `>> connexion de ${userN}`);
this.errorMessage(`Bonjour, ${userN}`);
const username = encrypt(this.state.name); // On encrypte le nom d'utilisateur
const password = encrypt(this.state.pwd); // On encrypte le mot de passe
//console.log(username, password)
const franck = Object(JSON.parse(await AsyncStorage.getItem("franck")))
const sessionDate = new Date(franck.session).getDay()
const todayDate = new Date().getDay()
if( sessionDate == todayDate && decrypt(username) == decrypt(await AsyncStorage.getItem("username")) && decrypt(password) == decrypt(await AsyncStorage.getItem("password"))) {
await AsyncStorage.setItem("franck", JSON.stringify(franck));
navigation.navigate('Main') // On navigue vers la page principale
//console.log(await AsyncStorage.getItem("franck"));
console.log("Fast connection");
} else {
try {
const response = await fetch(`link-to-the-api`); // On récupère les données de pronote
const franck = await response.json(); // On récupère les données de pronote
await AsyncStorage.setItem("franck", JSON.stringify(franck));
await AsyncStorage.setItem("username", username);
await AsyncStorage.setItem("password", password);
//console.log(await AsyncStorage.getItem("franck"));
navigation.navigate('Main'); // On navigue vers la page principale
console.log("Slow connection");
} catch {
this.setState({ disabledButton: false })
return this.errorMessage("Identifiant ou Mot de passe incorrect !"); // Si l'identifiant ou le mot de passe est incorrect, on affiche un message d'erreur
}
}
}
getID = async () => {
if ( dataReady == false ){
try {
const keptName = decrypt(await AsyncStorage.getItem("username"));
const keptPassword = decrypt(await AsyncStorage.getItem("password"));
console.log( "Existing user: "+ keptName)
this.setState({ keptName: keptName });
this.setState({ keptPassword: keptPassword });
dataReady = true;
} catch {
const keptName = "";
const keptPassword = "";
console.log( "No existing user: "+ keptName)
this.setState({ keptName: keptName });
this.setState({ keptPassword: keptPassword });
dataReady = true;
}
}
}
//keyboard listeners
UNSAFE_componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
UNSAFE_componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
// _keyboardDidShow () {
// alert('Keyboard Shown');
// }
_keyboardDidHide() {
NavigationBar.setVisibilityAsync("hidden");
NavigationBar.setBehaviorAsync('overlay-swipe');
NavigationBar.setButtonStyleAsync("light");
}
render() {
this.getID()
//checkbox 'se souvenir de moi'
function CheckBox() {
const [checked, onChange] = useState(false);
function onCheckmarkPress() {
onChange(!checked);
}
return (
<Pressable
style={[defaultCSS.checkboxBase, checked && defaultCSS.checkboxChecked]}
onPress={onCheckmarkPress}>
{checked && <Ionicons style={defaultCSS.check} name="checkmark" size={24} color="white" />}
</Pressable>
);
}
//checkbox 'Connexion rapide'
function CheckBox2() {
const [checked2, onChange2] = useState(false);
function onCheckmarkPress2() {
onChange2(!checked2);
}
return (
<Pressable
style={[defaultCSS.checkboxBase, checked2 && defaultCSS.checkboxChecked]}
onPress={onCheckmarkPress2}>
{checked2 && <Ionicons style={defaultCSS.check} name="checkmark" size={24} color="white" />}
</Pressable>
);
}
if (dataReady) {
return (
<>
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"}
enabled={true} style={defaultCSS.container} >
<View style={defaultCSS.container}>
<View style={defaultCSS.circle} />
<View style={{ marginTop: 64 }}>
<Image source={logo}
style={{ width: 100, height: 100, alignSelf: "center" }} />
</View>
<View style={{ marginHorizontal: 32 }}>
<Text style={defaultCSS.header}>Bonjour, </Text>
<Text style={defaultCSS.underHeader}>connectez-vous pour acceder à nos services</Text>
<TextInput defaultValue={this.state.keptName}
autoComplete="username"
style={defaultCSS.textInput}
placeholder="Nom d'utilisateur atrium"
placeholderTextColor={'#80786a'}
onChangeText={name => {
this.setState({ name })
}}
value={this.state.name}
/>
<TextInput defaultValue={this.state.keptPassword}
autoComplete="password"
secureTextEntry={true}
style={defaultCSS.textInput}
placeholder="Mot de passe atrium"
placeholderTextColor={'#80786a'}
onChangeText={pwd => {
this.setState({ pwd })
}}
value={this.state.pwd}
/>
{/* <CheckBox />
<Text style={defaultCSS.checkboxLabel}>Se souvenir de moi</Text>
<CheckBox2 />
<Text style={defaultCSS.checkboxLabel}>Connexion rapide</Text> */}
<View style={{ alignItems: "flex-end", marginTop: 64 }}>
<TouchableOpacity disabled={this.state.disabledButton}
activeOpacity={0.6}
style={!this.state.disabledButton ? defaultCSS.continue : defaultCSS.continueDisabled}
onPress={this.continue}>
<Ionicons name="arrow-forward-outline" size={24} color='#FFF' />
</TouchableOpacity>
</View>
</View>
<View style={defaultCSS.bottomContainer}>
<View style={defaultCSS.bottomBox}>
<Text style={defaultCSS.basicText}>Si vous avez oublié vos identifiants,
rien de grave, réinitialisez-les directement via Atrium!</Text>
</View>
<View style={defaultCSS.bottomBox}>
<Text style={defaultCSS.basicText}>
JDO-Copilot ne garde pas de données personnelles.</Text>
</View>
</View>
</View>
</KeyboardAvoidingView>
</>
);
} else {
return(
<View style={{backgroundColor: '#555555'}}>
</View>
);
}
}
}
export default withNavigation(Login);
I hope you could help me, thank you and I apologize for my bad english 🙂
2
Answers
Actually, using the ref prop didn't worked either, the solution was to wrap all my app.js in a function, export it as default and to export the AppContainer itself adding this at the end :
I hope that it can be useful for someone :)
You can use the "ref" property to set a reference to a specific navigation container, and then use that reference to navigate to different screens without using the "navigation" prop. To learn more about this method, you can check out the documentation on the React Navigation https://reactnavigation.org/docs/navigating-without-navigation-prop/