React Native first time user stacknavigator - react-native

I have a stacknavigator with a intro screen and a login screen.
export const StackerIntro = createStackNavigator({
Intro: {
screen: Intro,
navigationOptions: {
header: null
}
},
Login: {
screen: Login,
}
}, {
InitialRouteName: "Intro"
})
How can I make the initial route be "Login" if it isnt the users first time in the app?

For conditional rendering you might have to use switchNavigation.
1 .Save the key in async storage if the user logs in for the first time.
When the user comes again for the second time, get the key in navigation class using async-await and if you get the key then navigate to the desired location or else navigate to login.
Use SwitchNavigation for conditional navigation.
You can also try :
const routeName = () => {
var routeName =
global.isSignUpScreen == false || global.isSignUpScreen == undefined
? "LoginScreen"
: "SignUpScreen";
console.log("routeName >> " + routeName);
return routeName;
};
const SignUpStack = createStackNavigator(
{
LoginScreen: {
screen: LoginScreen,
navigationOptions: {
header: null
}
},
SignUpScreen: {
screen: SignUpScreen,
}
{
initialRouteName: routeName()
}
}
);

you dont need to set initialroute to be login. all you need to do is store a value in asyncstorage when the app is opened for the first time and for the second time when it comes to app.js check if that value is there are not. if its there then you need to go to login else go to intro.
Ex:
const value = await AsyncStorage.getItem("installSucess");
if(value === 'true'){
navigate to login
}else{
navigate to into
}
and in intro screen for the first time when you installed the app store value in asynstorage like
await AsyncStorage.setItem("installSucess", "true");

Related

Authentication Flow in React Native

I'm having issue creating the authentication flow of my app. What I actually want is to navigate user conditionally based on their role.
By default, I have created an AuthStack which is basically a stackNavigator and it has a login page. Once user logs in, we receive user's role through a network request. Next I navigate him to a simple home page that returns nothing but switchNavigator based on his role. Here's the code for better clarity.
const AuthStack = createStackNavigator({
Login: {
screen: Login
},
SignUp: {
screen: SignUp
},
Home: {
screen: Home
}
},
{
initialRouteName: 'Login',
headerMode: 'none'
});
const AppContainer = createAppContainer(AuthStack);
const Navigation = () => {
return <AppContainer />
}
When user logs in, I redirect him to Home screen shown in above mentioned stack. Here's the code in Home screen:
const Home = (props) => {
const AppContainer = createAppContainer(RootNavigator(props.user.role))
return <AppContainer />
}
Here I create a new app container (which might be the bad practice, advice please). And RootNavigator is a helper function which returns a switchNavigator:
export const RootNavigator = (user) => {
return createSwitchNavigator({
Admin: {
screen: AdminDrawerNavigator
},
Reporter: {
screen: ReporterDrawerNavigator
}
},
{
initialRouteName: user === 'admin'? 'Admin': 'Reporter'
})
}
This all works fine but it seems switchNavigator doesn't seem to work correctly. If I press back button of hardware, it goes back to login page. Thank you for your time. Please suggest the possible solution.
Try to reset you history navigation :
NavigationActions.reset({
index: 1,
actions: [NavigationActions.navigate({
routeName: 'Admin'
}),
]
});

How to load Home screen without showing login screen after login successful?

There are two stacknavigator for navigate with screens. I want to take condition for isloggedin or not. If loggedin user then select Appstack neither AuthStack. How to do this in this code? Is anyone can suggest me changes?
const AuthStack = createStackNavigator({
Welcome: {screen: WelcomeScreen,
navigationOptions: {
header:null
}},
Login: LoginScreen,
Signup: SignupScreen,
Forgot:ForgotScreen,
});
const AppStack =createStackNavigator(
{
Dashboard: DashboardScreen,
ScanScreen:ScanScreen,
});
export default createSwitchNavigator(
{
App:AppStack,
Auth:AuthStack,
},
{
initialRouteName:'Auth',
}
);
Where you define, and export your routers, define and export a function, which accepts a boolean parameter 'isLoggedIn', and returns the respective router, based on the value of isLoggedIn.
const AuthStack = createStackNavigator({
Welcome: {screen: WelcomeScreen,
navigationOptions: {
header:null
}},
Login: LoginScreen,
Signup: SignupScreen,
Forgot:ForgotScreen,
});
const AppStack =createStackNavigator(
{
Dashboard: DashboardScreen,
ScanScreen:ScanScreen,
});
// This is the function that does the work, it will render
// the either Auth or App router, based on if you're logged in.
export const createRootNavigator = (isLoggedIn = false) => {
if (isLoggedIn === false) {
return <AuthStack/>
} else {
return <AppStack/>
}
};
Then in your app component (or wherever you're rendering your router), we need to import this function, and pass into it whether or not the user is currently logged in.
'file-above.js' is the code above, i.e. the code you gave in your example as well.
Rough example below.
// We import the file
import {createRootNavigator} from './file-above.js'
class App extends Component {
constructor (props) {
super(props);
this.state({
// Switching this will switch your stack router.
loggedIn: false
});
}
render () {
const {loggedIn} = this.state;
// Here we're rendering the router, passing in the
// loggedIn variable from state, this will dictate if the
// Auth or App router is rendered.
return createRootNavigator(loggedIn);
}
}
What is missing here of course is the 'loggedIn' field on the app components state switching, play with the code above, changing the value of loggedIn via the constructor definition, then go hook up to your login form(s), etc.

Finish current component while navigating to next component using React Native Navigation?

I wanted to close the current component completely while navigating to next component in react-native.
I am using react-navigation for navigating between screens.
Scenario is, I am having two js in my project, Login.js and Home.js. When user logs in into the app it saves the credentials in the AsyncStorage. Every-time when user comes to Login Screen it checks for whether user is logged in already or not. If the user is logged in then app will directly navigate you to the Home page, at this action I want to close the login screen completely.
Currently with my implementation the Login screen remains in to the navigation stack. When I press back from the Home page the app should be closed completely and should not relaunch with login screen again.
Here is my StackNavigator code :
const navigationStack = createStackNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
For navigating :
this.props.navigation.navigate('Home');
Please let me know what I am doing wrong with my existing code?
You can implement this by multiple ways. Like using replace or reset action on stack Navigator, or using switch Navigator instead of stack Navigator.
Using Reset: (Empty stack and navigate to specified screen)
import { StackActions, NavigationActions } from 'react-navigation';
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Home' })],
});
this.props.navigation.dispatch(resetAction);
Using replace: (replace current screen with the specified screen)
this.props.navigation.replace("Home");
Using Switch Navigator:(Recommended)
const navigationStack = createSwitchNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
// Navigate to screen
this.props.navigation.navigate("Home");
This can be achieved without having to add back handling code to each and every screen by modifying the getStateForAction method of the particular StackNavigator's router.
const navigationStack = createStackNavigator(
{
Login: {
screen: LoginScreen
},
Home: {
screen: HomeScreen
},
},
);
The getStateForAction method can be modified to achieve this
const defaultStackGetStateForAction =
navigationStack.router.getStateForAction;
navigationStack.router.getStateForAction = (action, state) => {
if(state.index === 0 && action.type === NavigationActions.BACK){
BackHandler.exitApp();
return null;
}
return defaultStackGetStateForAction(action, state);
};
the state.index becomes 0 only when there is one screen in the stack.
You can check with this Back Handling

How to redirect to login screen in react navigation tab navigator with redux

I'm trying to figure out how to go about coding this.
I'm using a React Navigation TabNavigator as my main navigator and am also using Redux to manage my app and user's auth state.
One of the tabs has content that can only be displayed if the user is logged in so what I am trying to do is when they press that tab, if they are not logged in yet, I want to redirect to or pop a modal on top with a login/registration screen.
After they have successfully logged in and their content pulled down, then I want to show the screen in the tab that they were originally trying to view.
So how would you go about this?
From my understanding with the TabNavigator, after the initial load then componentWillMount does not run each time I click on that tab so I can't check my auth state there and react.
Is there a way to intercept the tab press otherwise and be able to check my auth state from there before loading the view for that tab?
First, you need to activate lazy options on TabNavigator config, eg:
const AppNavigator = TabNavigator(
{
Home: { screen: HomeScreen },
LoginScreen: { screen: LoginScreen },
/* the screen needed auth */
AddPost: {screen: AddPostScreen},
...
},
{
lazy: true,
...
})
Second, I add this package react-navigation-is-focused-hoc
$ yarn add react-navigation-is-focused-hoc
It's for the checking active screen, on react render AppNavigator add some props:
...
import { updateFocus } from 'react-navigation-is-focused-hoc';
...
return (
...
<AppNavigator
onNavigationStateChange={(prevState, currentState) => {
updateFocus(currentState);
}}
/>
...
);
The last, add isFocused to your Authenticated screen (AddPostScreen):
import { withNavigationFocus } from 'react-navigation-is-focused-hoc';
...
#withNavigationFocus('AddPostScreen')
class AddPostScreen extends React.Component {
static navigationOptions = () => ({
/* Your navigation options */
})
componentWillReceiveProps(nextProps) {
const { isFocused, auth: { signedIn }, navigation: { navigate } } = this.props;
if (!isFocused && nextProps.isFocused && !signedIn) {
navigate('LoginScreen');
}
}
shouldComponentUpdate(nextProps) {
const { isFocused } = this.props;
if (isFocused && !nextProps.isFocused) {
return true;
}
// Don't update if the screen is not focused
if (!isFocused && !nextProps.isFocused) {
return false;
}
// Update the screen if its re-enter
return !isFocused && nextProps.isFocused;
}
render() {
return (
/* return authenticated component */
...
signedIn (boolean) is state from your auth reducers

sending the navigation params as props to other component

I am using RN v0.46.4 , react-navigation and sendbird.
I am developing a chat application.
What I am trying to do is that navigating the user to Msg screen with two params item (contain user info) and createdChannel .
They are passed but only once i.e. each time I navigate to Msg screen for different users , I receive the same value on which I have presses first.
Chat Screen
_chat(item){
// console.log(item);
const { navigate } = this.props.navigation
var userIds = [item.id,1];
sb = new SendBird({appId: APP_ID});
sb.connect(1, function(user, error) {
//console.log(user);
sb.GroupChannel.createChannelWithUserIds(userIds, true, item.firstname, function(createdChannel, error) {
if (error) {
console.error(error);
return;
}
//console.log(createdChannel);
navigate('Msg', { item, createdChannel })
});
Also, when I console createdChannel in _chat function , it gives the roght information as expected.
But when I console it in Msg screen , I receive only the first createdChannel created, already told above.
Msg Screen
super(props);
console.log(props.navigation.state.routes[1].params.createdChannel);
My router structure:
const CustomTabRouter = TabRouter(
{
Chat: {
screen: ChatStack,
path: ""
}
}
ChatStack
const ChatStack= StackNavigator({
Chat:{screen: Chats,
navigationOptions: {
header: null,
}},
Msg: {
screen: Msg,
navigationOptions: ({ navigation}) => ({
title: `${navigation.state.params.item.firstname} ${navigation.state.params.item.lastname}`,
tabBarVisible: false
})
},
})
In your Msg screen's constructor function, try accessing the item and createdChannel by calling props.navigation.state.params.createdChannel.
super(props);
console.log(props.navigation.state.params.createdChannel);
console.log(props.navigation.state.params.item);
I found myself in a similar situation. In my case the problem was because passing params to nested screen was intentionally removed from the lib (1.0.0-beta21 to 1.0.0-beta22).
Note: But as of now, react-navigation version is v1.5.2
https://github.com/react-navigation/react-navigation/issues/3252
Anyway, my quick and dirty solution is to use screenProps to inject props into screen.
const ChatStack= StackNavigator({
Chat:{ screen: Chats, ... },
Msg: { screen: Msg, ... },
});
// Instead of exporting ChatStack directly, create a wrapper and pass navigation.state.params to screenProps
export default (props) => {
const params = props.navigation.state.params;
// const params = _.get(props, 'navigation.state.params');
return (
<ChatStack screenProps={params} />
);
};
Now all screens under ChatStack can access those props injected.