React Native StackNavigator initialRouteName - reactjs

In React Native Navigation library 'react-navigation'
How could I set StackNavigator initialRouteName by AsyncStorage?
function getInitialScreen() {
AsyncStorage.getItem('initialScreen')
.then(screenName => {
return (screenName)
? screenName
: 'Login';
})
.catch(err => {});
}
const Navigator = StackNavigator({
Splash: { screen: Splash },
Login: { screen: Login },
WebPage: { screen: WebPage }
}, {
initialRouteName: getInitialScreen()
});

Changing InitialRouteName with multiple Route depending upon your requirement. I have got it working this way.
create router file import all your screens.
export a stateless function call it createRootNavigator with params as (load="<Your initial screen>")
export const createRootNavigator = (load="<Your Initial Screen>") => {
return stackNavigator({
Initialize: {
screen: Initialize,
},
Main: {
screen: Main,
},
{
initialRouteName: load
}
})
}
In your main app,
state = {
load: "<Your Initial Screen>"
}
eg:
state = {
load: "Initialize" // value is string
}
Set the state accordingly in componentDidMount() method. And finally render new layout.
render() {
const Layout = createRootNavigator(this.state.load);
<Layout />
}
The above method worked fine for me. Hope it helps somebody.

I’ve also had this problem and currently the only good solution is the following example:
const RootNavLogged = StackNavigator({
...
},{
initialRouteName : 'Home'
});
const RootNav = StackNavigator({
...
},{
initialRouteName : 'Login'
});
class App extends Component {
render(){
if (this.props.userLogged == true ){
return (
<RootNavLogged/>
)
} else {
return(
<RootNav/>
)
}
}
}

Full Solution from React Native Navigation on Restart:
const Navigator = StackNavigator({
InitialScreen: {
screen: InitialScreen
},
Splash: {
screen: Splash
},
LanguageStartup: {
screen: LanguageStartup
},
Login: {
screen: Login
},
Register: {
screen: Register
}
}, {initialRouteName: 'InitialScreen'});
export default Navigator;
My Initial Screen
import React, {Component} from 'react';
import {connect} from 'react-redux';
import * as GeneralPref from './../preferences/GeneralPref'
import Log from './../utils/Log'
import {AsyncStorage, View} from 'react-native';
import * as Pref from './../preferences/Preferences';
import {NavigationActions} from 'react-navigation'
const TAG = 'InitialScreen'
class InitialScreen extends Component {
static navigationOptions = {
header: false
};
componentWillMount() {
Log(TAG+' Mount')
const {navigate} = this.props.navigation;
GeneralPref
.getInitialScreen()
.then(value => {
Log(TAG+' Initial',value)
if (value != null) {
Log(TAG+' Initial',value)
return value
} else {
Log(TAG+' No Initial','Splash')
return 'Splash'
}
})
.then(screenName => this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName: screenName})]
})))
.catch(err => {
Log(TAG+' Initial Error',value)
this.props.navigation.dispatch(NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName: 'Splash'})]
}))
});
}
render() {
return null;
}
}
export default InitialScreen;
then in Language Screen
changeLanguageTo(language) {
Log(TAG+'Change Language', "Change Language To: " + language.code);
// Log(TAG, 'Current State');
Log(TAG+' Language State', language);
GeneralPref.setInitialScreen('Login');
this
.props
.actions
.changeLanguage(language);
I18nManager.forceRTL(true);
// Immediately reload the React Native Bundle
RNRestart.Restart();
};

Related

Export react native stackNavigator after checking for value in AsyncStorage

I have an application where a token get stored once the users open the app for the first time, and I need to check for that token when the app starts and create the navigator,
Approach 1
const getValue = async () => {
try {
value = await AsyncStorage.getItem('token').then((value) =>{
console.log(value)
})
} catch (error) {
console.log(error.message);
}
};
if (token != undefined) {
stackNavigator = createStackNavigator({
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
} else {
stackNavigator = createStackNavigator({
Home:Home,
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
}
In the first approach, it always renders the home screen even if the token is stored in the application
Second approach
const getValue = async () => {
try {
value = await AsyncStorage.getItem('token').then((value) =>{
if(value == null){
stackNavigator = createStackNavigator({
Home:Home,
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
}else{
stackNavigator = createStackNavigator({
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
}
})
} catch (error) {
console.log(error.message);
}
};
This approach throws Cannot read property of router undefined
Is there a way to get this to work?
Navigation.js
export const getValue = async () => {
let VALUE;
try {
await AsyncStorage.getItem('token')
.then((value) =>{
if(value == null){
VALUE = stackNavigator = createStackNavigator({
Home:Home,
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
}else{
VALUE = stackNavigator = createStackNavigator({
Products: Products,
Product: Product
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
})
}
})
} catch (error) {
console.log(error.message);
}
return VALUE;
};
App.js
import { getValue } from './layout/navigation/Navigation';
let Navigation = createAppContainer(getValue());
class App extends React.Component {
render() {
return (<Navigation/>)
}
}
Create Routing
stackNavigator = createStackNavigator({
Home:Home,
Products: Products,
Product: Product,
Login : Login
},
{
headerMode: 'none',
initialRouteName : 'Home', // provide initial route : app will open with Home page
navigationOptions: {
headerVisible: false
},
})
// Export it with createAppContainer
export default createAppContainer(stackNavigator);
Import it to your App.js and use it as <Router/>
import Router from 'Application/navigation/routes';
Now what you can do is when user going to login then store their token to AsyncStorage and then redirect to home page.
In your home page you can add your token is exist or not code in mount lifecycle if you are not getting your token from storage then you can navigate your route to login screen.

Navigation if not authorized does not work with react-native?

Problem:
I have created a react native application in there I am checking if not authorized I am removing the async storage and redirecting to login. To handle the Axios easily I have created an HTTP client file like this.
import axios from "axios";
import { AsyncStorage } from "react-native";
import { NavigationActions } from 'react-navigation';
// TODO: Replace this with actual JWT token from Keycloak
axios.defaults.headers.post["Content-Type"] = "application/json";
// Create axios instance for api calls
var instance = null;
export const setAuth = async () => {
const token = await AsyncStorage.getItem("jwt");
instance = axios.create({
baseURL: "",
timeout: 150000,
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json"
}
});
instance.interceptors.response.use(
function(response) {
return response;
},
async function(error) {
console.log(error);
if (error.response.status) {
if (error.response.status === 401) {
AsyncStorage.removeItem('jwt')
AsyncStorage.removeItem("user");
NavigationActions.navigate({
routeName: 'login'
});
} else {
return error;
}
}
}
);
};
export const Get = (route, data) => {
function getData() {
return instance.get(
route,
data == null ? { data: {} } : { data: JSON.stringify(data) }
);
}
if (instance) return getData();
return setAuth().then(getData);
};
export const Post = (route, data) => {
function postData() {
return instance.post(route, JSON.stringify(data));
}
if (instance) return postData();
return setAuth().then(postData);
};
I am accessing this HTTP client file inside the redux logic function. So this is outside of the component. Problem Now I have faced is It is removing the Asyncstorage but the navigation does not seem to work correctly.
How I create My routes is this.
import React, { Component } from "react";
import { createAppContainer } from "react-navigation";
// import { createStackNavigator } from "react-navigation";
import { createBottomTabNavigator } from "react-navigation-tabs";
import { createStackNavigator } from "react-navigation-stack";
import IonIcon from "react-native-vector-icons/Ionicons";
import { Image } from "react-native";
import LoginScreen from "./components/Login/Login";
import HomeScreen from "./components/Home/Home";
import SettingsScreen from "./components/Settings/Settings";
import FinesScreen from "./components/Fines/Fines"
import ChangePassword from "./components/Changepassword/Changepassword";
const SettingsTab = createStackNavigator(
{
settings: { screen: SettingsScreen },
changePassword: { screen: ChangePassword }
},
{
initialRouteName: "settings",
headerMode: "none"
},
(navigationOptions = {
headerMode: "none"
})
);
const TabNav = createBottomTabNavigator(
{
home: {
screen: HomeScreen,
navigationOptions: {
tabBarLabel: false,
tabBarIcon: () => (
<Image source={require("../assets/invalid-name.png")} />
)
}
},
fines: {
screen: FinesScreen,
navigationOptions: {
tabBarLabel: false,
headerMode: "none",
tabBarIcon: () => (
<Image source={require("../assets/icon-service-fines.jpg")} />
)
}
},
settings: {
screen: SettingsTab,
navigationOptions: {
tabBarLabel: false,
headerMode: "none",
tabBarIcon: () => <Image source={require("../assets/settings.png")} />
}
}
},
{
tabBarPosition: "bottom",
swipeEnabled: true,
animationEnabled: true,
tabBarOptions: {
activeTintColor: "#FFFFFF",
inactiveTintColor: "#F8F8F8",
borderTopColor: "transparent",
style: {
backgroundColor: "#fffff",
borderTopColor: "transparent",
paddingTop: 0
},
indicatorStyle: {
borderBottomColor: "#87B56A",
borderBottomWidth: 2
},
tabStyle: {
justifyContent: "center"
}
}
}
);
const MainNavigator = createStackNavigator(
{
login: { screen: LoginScreen },
tab: { screen: TabNav }
},
{
initialRouteName: "login",
headerMode: "none"
},
(navigationOptions = {
headerMode: "none"
})
);
const AppContainer = createAppContainer(MainNavigator);
export default AppContainer;
An I used it in the app.js file like this.
import React from "react";
// import Login from "./src/components/Login/Login";
// import Register from "./src/components/Register/Register";
import Route from "./src/route";
import { Provider } from "react-redux";
import { createLogicMiddleware } from "redux-logic";
import { createStore, applyMiddleware, compose } from "redux";
import NavigationService from "./src/services/navigationService";
import reducers from "./src/reducers";
import services from "./src/services";
const logicMiddleware = createLogicMiddleware(services, {});
const middleware = applyMiddleware(logicMiddleware);
const composeEnhancers = compose;
const enhancer = composeEnhancers(middleware);
let store = createStore(reducers, enhancer);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<Route
// ref={navigatorRef => {
// NavigationService.setNavigator(navigatorRef);
// }}
></Route>
</Provider>
);
}
}
Can someone help me to solve this issue? Thank you.
NavigationActions return an object that can be sent to the router using navigation.dispatch() method
You can achieve solution in multiple ways. Here I am going to explaining without Props
First you need to create a service
let _navigator;
function setNavigator(navRef) {
_navigator = navRef;
}
function navigate(navAction) {
_navigator.dispatch(
navAction
);
}
// add other navigation functions that you need and export them
export default {
navigate,
setNavigator,
};
In App container create a ref for
<AppContainer
ref={navigatorRef => {
NavigationService.setNavigator(navigatorRef);
}}
/>
Use in your http client file
import NavigationService from 'path-to-NavigationService.js';
NavigationService.navigate(NavigationActions.navigate({
routeName: 'login'
}););
expo reference

React Native - NavigationActions.navigate() not navigating from within redux

I have two navigators one is stackNavigator and another is drawerNavigator.
what I want to do is dispatch an action and login is successfull and redirect the user to drawer navigator. I have used react-navigation.
What I have done is I am dispatching the action login success in saga.
Using NavigationActions.navigate({ routeName: 'drawerStack' }) to dispatch the action.
The action dispatches successfully but it doesn't navigate to drawerNavigator as shown in the picture below. What am I doing wrong?
saga.js
function* watchLoginRequest() {
while (true) {
const { state } = yield take(LOGIN_REQUEST);
try {
const payload = {
state
};
const response = yield call(loginCall, payload);
yield put(loginSuccess(response));
yield setUser(response.user);
yield put(NavigationActions.navigate({ routeName: 'drawerStack' }));
} catch (err) {
yield put(loginFailure(err.status));
}
}
}
drawerNavigation.js
// drawer stack
const DrawerStack = DrawerNavigator({
testComponent: { screen: TestComponent },
});
const DrawerNav = StackNavigator({
drawerStack: { screen: DrawerStack }
}, {
headerMode: 'float',
navigationOptions: ({ navigation }) => ({
headerStyle: { backgroundColor: 'green' },
title: 'Logged In to your app!',
headerLeft: <Text onPress={() => navigation.navigate('DrawerOpen')}>Menu</Text>
})
});
export default DrawerNav;
loginNavigation.js
// login stack
const LoginStack = StackNavigator({
startScreen: { screen: StartScreen },
loginScreen: { screen: LoginScreen },
personalInformation: { screen: PersonalInformation },
vehicleInformation: { screen: VehicleInformation },
availability: { screen: Availability },
selectRegisteration: { screen: SelectRegisteration },
serviceAddress: { screen: ServiceAddress },
}, {
headerMode: 'none',
transitionConfig: TransitionConfiguration
});
export default LoginStack;
ReduxNavigation.js
class ReduxNavigation extends React.Component {
constructor(props) {
super(props);
const { dispatch, nav } = props;
const navigation = ReactNavigation.addNavigationHelpers({
dispatch,
state: nav
});
this.state = {
loggedInStatus: false,
checkedSignIn: false
};
}
componentWillMount() {
isSignedIn()
.then(res => {
if (res !== null) {
this.setState({
loggedInStatus: true,
checkedSignIn: true
});
} else {
console.log(res);
}
})
.catch(err => console.log(err));
}
render() {
return <LoginNavigation navigation={this.navigation} />;
}
}
const mapStateToProps = state => ({ nav: state.nav });
export default connect(mapStateToProps)(ReduxNavigation);
To navigate to TestComponent you want your routeName to be testComponent, not the drawer stack. You navigate to specific screens, not navigation components.

Reset StackNavigator when change TabNavigator

I have a problem with stack navigator ... I have two navigations
tabNavigator
-> main
-> stackNavigator
-> page 1
-> page 2
So I would like when i am on page 2 and I click on main (tabNavigator) my stacknavigator reset to page 1 ...
App.js
import React, { Component } from 'react';
import { Text, Button, View } from 'react-native';
import { TabNavigator, StackNavigator, NavigationActions} from 'react-navigation';
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'PageOne'})
]
})
class PageOneScreen extends Component {
render() {
return (<View><Text> PageOne </Text><Button title="next" onPress={() => this.props.navigation.navigate('PageTwo')} /></View>);
}
}
class PageTwoScreen extends Component {
render() {
return (<View><Text> PageTwo </Text><Button title="reset" onPress={() => this.props.navigation.dispatch(resetAction)} /></View>);
}
}
class MainScreen extends Component {
render() {
return (<Text> Main </Text>);
}
}
const stackNav = StackNavigator({
PageOne: {screen: PageOneScreen},
PageTwo: {screen: PageTwoScreen}
});
const tabNav = TabNavigator({
Main: {screen: MainScreen},
Stack: {screen: stackNav}
});
export default tabNav;
But I do not manage
Use tabBarOnPress in tabNavigator which is a callback to handle tap events.
From the above code, add the navigationOption used by the Tab Navigator as coded below. Hope this will solve your problem
const tabNav = TabNavigator({
Main: {screen: MainScreen},
Stack: {
screen: stackNav,
navigationOptions: ({navigation}) => ({
tabBarOnPress: (scene, jumpToIndex) => {
navigation.dispatch(
NavigationActions.navigate({
routeName: 'Stack', //which is your route to be reset
action: NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'PageOne' }) //which is the default stack to be shown on pressing the tab
]
})
})
);
}
})
}
});

How to implement a multi step login in react or react native

I would like to implement a multi step login, similar to slack (it first asks the domain name, then the email, and later the password) in react and react-native.
I would like to know what are the best practices to do it?
Should I use a router/navigation solution like ReactNavigation https://github.com/react-community/react-navigation?
That's one way to do it, sure. The way I would do it is a single component:
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = { stage: 0 };
}
onDomainSubmit(data) {
this.props.getDomain(data.domain).then((domain) => {
this.setState({ domain, stage: 1 });
});
}
render() {
const { stage, domain } = this.state;
if (stage === 0) {
return <GetDomainForm onSubmit={ this.onDomainSubmit }... />;
} else if (stage === 1) {
return <LoginToDomainForm domain={ domain }... />;
}
}
}
getDomain is an action creator which has been injected into the component via react-redux's connect - although that's not necessary.
there's less headache that way, and everything you need is contained within one component.
you can use SwitchNavigator.
import React, { Component } from 'react';
import { StackNavigator, TabNavigator, SwitchNavigator } from 'react-navigation';
import AuthLoadingScreen from '../views/users/auth';
import LoginScreen from '../views/users/login';
import RegisterScreen from '../views/users/register';
import ResetPasswordScreen from '../views/users/resetPassword';
import MainTabNavigator from './MainTabNavigator';
export const AuthStack = StackNavigator({
Auth: { screen: AuthLoadingScreen, navigationOptions: { header: null } },
Login: { screen: LoginScreen, navigationOptions: { header: null } },
Register: { screen: RegisterScreen, navigationOptions: { header: null } },
ResetPassword:{ screen: ResetPasswordScreen, navigationOptions: { header: null } }
},{
initialRouteName: "Auth"
});
export const AppStack = TabNavigator(
{ screen: MainTabNavigator, },
{ navigationOptions: {
headerStyle: { backgroundColor: '#f4511e', },
headerTintColor: '#fff',
headerTitleStyle: { fontWeight: 'bold', },
},
}
);
export const createRootNavigator = () => {
return SwitchNavigator(
{
SignedIn: { screen: AuthStack },
SignedOut: { screen: AppStack }
},
{
initialRouteName: "SignedIn"
}
);
};

Resources