React-navigation redirect from a signIn screen - react-native

I have 3 screens, Home.js, SignIn.js and CreateElement.js. People must be logged in to use CreateElement.js. So when some user is on Home and wants to use CreateElement the screen should redirect them to SignIn.js and then when the user is logged SignIn should continue the flow and redirect them to CreateElement.js.
My code doesn't work and it is the following
return (
<NavigationContainer linking={Linking}>
<AuthChecker>
<LanguageHandler>
<Stack.Navigator headerMode="none" >
<Stack.Screen name="Home" component={HomeScreen} />
{loggedIn
? <>
<Stack.Screen name="Create" component={CreateElementScreen} />
</>
: <>
<Stack.Screen name="SignIn" component={SignInScreen} />
</>
}
</Stack.Navigator>
</LanguageHandler>
</AuthChecker>
</NavigationContainer>
);
}
loggedIn is in the redux store.
Home.js has:
if (loggedIn)
navigate('Create')
else
navigate('SignIn')
and SignIn only change loggedIn to TRUE.
My first solution was create a private component like this one.
// #Vendors
const PrivateRoute = ({ loggedIn, children}) => {
const { navigate } = useNavigation();
useLayoutEffect(() => {
if (!loggedIn)
navigate('SignIn');
console.log('Component is mounted in the DOM');
}, []);
if (loggedIn)
return {children} //In This case <CreateElement/>
else
return <></>
}
const mapStateToProps = ({ auth }) => ({
loggedIn: auth.loggedIn
});
export default connect(mapStateToProps)(PrivateRoute);
My solution work on Android and IOS but when I use react-native-web, the code work but if I enter by URL to CreateElement the privateElement doesn't redirect to me
Any suggestion?

Have you checked whether the loggedIn variable is updated every time after the data is updated?
You might need a useState hook for updating the component.
Something like this:
const [isAuthenticated] = useState(loggedIn);
return (
<NavigationContainer linking={Linking}>
<AuthChecker>
<LanguageHandler>
<Stack.Navigator headerMode="none">
<Stack.Screen name="Home" component={HomeScreen} />
{isAuthenticated ? (
<>
<Stack.Screen name="Create" component={CreateElementScreen} />
</>
) : (
<>
<Stack.Screen name="SignIn" component={SignInScreen} />
</>
)}
</Stack.Navigator>
</LanguageHandler>
</AuthChecker>
</NavigationContainer>
);

Related

Navigate between two stack Navigators after login in react native

This is my first stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Home" component={MainScreen} />
<Stack.Screen name="Schools" component={SchoolsScreen} />
<Stack.Screen name="Setting" component={SettingScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Saved" component={SavedData} />
<Stack.Screen name="Profile2" component={Profile2} />
<Stack.Screen name="SchoolDetails" component={SchoolDetailsScreen} />
<Stack.Screen name="Bottom" component={BottomTab} />
</Stack.Navigator>
</NavigationContainer>
This is my second stack
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false,
}}
>
<Stack.Screen name="Login" component={AuthLogin} />
<Stack.Screen name="Register" component={AuthRegister} />
<Stack.Screen name="Forget" component={ForgetPassword} />
</Stack.Navigator>
</NavigationContainer>
THis is my app.js file
const [auth, setAuth] = useState(false);
useEffect(() => {
(async () => {
const value = await AsyncStorage.getItem("isLoggedin");
console.log(value);
setAuth(value);
})();
}, []);
return (
<SafeAreaView style={{ flex: 1 }}>
<NativeBaseProvider config={config}>
{auth == "true" ? <InsideStack /> : <OutsideStack />}
</NativeBaseProvider>
</SafeAreaView>
);
}
I want to navigate to home screen from login screen , after function call
// navigation.push("Home");
const storeData = async (value) => {
try {
await AsyncStorage.setItem("isLoggedin", JSON.stringify(true));
} catch (e) {
// saving error
}
};
storeData();
navigation.push("Home");
}}
But i got error:
Do you have a screen named 'Home'?
this is the simple code for this. you have to use redux or context api for gloal state management. here is a min example ;
import {NavigationContainer} from '#react-navigation/native';
import React from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {GetUserContacts} from '../logic/getcontact';
import {setContacts} from '../store/actions/ContactActions';
import AuthNavigation from './AuthNavigation';
import RootNavigation from './RootNavigation';
const AppContainer = () => {
const {is_logged_in} = useSelector(state => state.persistedReducer);
const dispatch = useDispatch();
if (is_logged_in) {
GetUserContacts('PK').then(data => {
dispatch(setContacts(data));
});
}
return (
<NavigationContainer>
{is_logged_in ? <RootNavigation /> : <AuthNavigation />}
</NavigationContainer>
);
};
export default AppContainer;
after user logged in you have to make that is_logged_in true. so the navigator changes itself. you will have to persist this so when application restart the user dont need to login again

Mobx update token and Navigate to the Auth Screen

I am new with Mobx and I want to store token after login screen and navigate to dashboard which is behind the Auth Navigator.
const AppStack = () => {
const { getToken } = useTokenStore()
const [userToken, setUserToken] = React.useState(false)
useEffect(() => {
console.log("<<<<< IS LOGGED IN STATE >>>>>>")
console.log(userToken)
console.log("<<<<< IS LOGGED IN STATE >>>>>>")
setUserToken(getToken())
console.log("<<<<< NEW TOKEN IN STATE >>>>>>")
console.log(userToken)
console.log("<<<<< NEW TOKEN IN STATE >>>>>>")
}, [userToken])
return (
<Stack.Navigator
screenOptions={{
headerShown: false,
}}
initialRouteName="login"
>
{!userToken ? (
<>
<Stack.Screen name="login" component={LoginScreen} />
<Stack.Screen name="signup" component={SignupScreen} />
<Stack.Screen name="resetpassword" component={ResetPasswordScreen} />
<Stack.Screen name="demo" component={DemoScreen} />
<Stack.Screen name="demoList" component={DemoListScreen} />
<Stack.Screen name="welcome" component={WelcomeScreen} />
</>
) : (
<>
<Stack.Screen name="dashboard" component={DashboardScreen} />
</>
)}
</Stack.Navigator>
)
}
I get the error ERROR The action 'NAVIGATE' with payload {"name":"dashboard"} was not handled by any navigator. Please advice me on the best way to change the value of the mobx stored data in realtime and navigate

React Native: The action 'NAVIGATE' with payload (blahblah) was not handled by any navigator. Do you have a screen named 'Home'?

I am attempting to have the Login Screen change to the Home Screen when I click the Log In button. The user has already been created and shows in both the authentication tab of Firebase, as well as the Cloud Firestore database I created for it. I think there is something wrong with my Navigation section, but I don't believe I am using a nested navigator... Here is the code on App.js for the navigation..If anyone can take a look at this code and see if you notice any issues with it, that would be appreciated. Not sure where to turn from here.
<NavigationContainer>
<Stack.Navigator>
{ user ? (
<Stack.Screen name="Home">
{props => <HomeScreen {...props} extraData={user} />}
</Stack.Screen>
) : (
<>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Registration" component={RegistrationScreen} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
Another user has pointed out to me that it is not rendering the Home Screen because a user it not being assigned when I click the Log In button. Here is the method I am using for the Log In button. Does anyone see any issues here?
const onLoginPress = () => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
const uid = response.user.uid
const usersRef = firebase.firestore().collection('users')
usersRef
.doc(uid)
.get()
.then(firestoreDocument => {
if (!firestoreDocument.exists) {
alert("User does not exist anymore.")
return;
}
const user = firestoreDocument.data()
navigation.navigate('Home', {user})
})
.catch(error => {
alert(error)
});
})
.catch(error => {
alert(error)
})
}
Thanks for your help!
when the user is not defined, the Home screen is not rendered, so that's why it's giving you an error when you navigate from LoginScreen to Home. You can just render the Home screen unconditionally, and set the initialScreen to Login.
<NavigationContainer>
<Stack.Navigator initialRouteName="LoginScreen">
<Stack.Screen name="Home">
{props => <HomeScreen {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Registration" component={RegistrationScreen} />
</Stack.Navigator>
</NavigationContainer>

Navigating between different stackscreen in react-navigation 5

isSignedIn ? (
<>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</>
) : (
<>
<Stack.Screen name="SignIn" component={SignInScreen} />
<Stack.Screen name="SignUp" component={SignUpScreen} />
</>
)
How can I navigate from signInScreen to HomeScreen.
You can make a context to handle the authentification flow and on your app launch check if a token or anything is already set.
const Index = () => {
const [authState, dispatch] = useAuthContext();
useEffect(() => {
const boostrapAsync = async () => {
let userToken = null;
try {
userToken = await AsyncStorage.getItem('userToken');
} catch (e) {
console.log('Restoring token failed');
}
restoreToken({ token: userToken }, dispatch);
};
boostrapAsync();
}, []);
if (authState.isLoading) {
return (
<SplashScreen />
);
}
return (
<Root>
<NavigationContainer>
<UIProvider reducer={UIReducer} initialState={initialStateUIReducer}>
{authState.userToken ? (
<UserProvider reducer={UserReducer} initialState={initialStateUserReducer}>
<AppNavigation />
</UserProvider>
) : <AuthNavigation />}
</UIProvider>
</NavigationContainer>
</Root>
);
};
export default Index;
These problems can be solved with a splash screen. I think you should collect all screens under one tag. Then, make a splash screen. After that, you can control the parameter is called isSignedIn in the SplashScreen. Then you navigate the screen which you want.

Navigation can't find screen if states condition is in Stack.Navigator

When I want to navigate from Home screen to Login screen I get error:
ExceptionsManager.js:173 The action 'NAVIGATE' with payload {"name":"Login"} was not handled by any navigator. Do you have a screen named 'Login'?
routes.js:
import LoginScreen from '../screens/login/login';
import HomeScreen from '../screens/home/home';
const Stack = createStackNavigator();
const App = () => {
const [userToken, setuserToken] = React.useState(null);
React.useEffect(() => {
_bootstrapAsync = async () => {
const token = await AsyncStorage.getItem('token');
if (userToken) {
setuserToken(token)
}
}
_bootstrapAsync()
})
return (
<NavigationContainer>
<Stack.Navigator>
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
)}
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
home.js:
import { AsyncStorage } from 'react-native';
import { Container, Text, Button, Content } from 'native-base';
const HomeScreen = (props) => {
const { navigation } = props
handleLogout = async () => {
await AsyncStorage.clear();
navigation.navigate('Login');
};
return (
<Container>
<Content>
<Button full onPress={handleLogout}>
<Text>Log Out</Text>
</Button>
</Content>
</Container>
);
}
export default HomeScreen;
If in routes.js I remove userToken states condition, and in Stack.Navigator is only:
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
I can successfully navigate from Home to Login screen.
But this approach is not good, because I need to have checking if token is present in Storage.
How can I solve this issue?
You will not able to find the Login route until your condition is true, that's the reason you are not able to find the Login route
change
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
)}
to
{userToken == null ? (
<Stack.Screen name="Login" component={LoginScreen} />
) : (
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
)}
Hope this helps!