I'd like to redirect a user to a view after login a user in with GoogleSignin instead of rendering a view
import React, { Component } from 'react';
import { AppRegistry, Platform, Text, Image, TouchableOpacity, View } from 'react-native'
import { StackNavigator, TabNavigator, NavigationActions } from 'react-navigation'
import { GoogleSignin, GoogleSigninButton } from 'react-native-google-signin'
import DashboardScreen from './Components/DashboardScreen'
// Routes
export const MyNavigator = StackNavigator({
DashboardScreen: {
name: 'DashboardScreen',
description: 'Dasboard available once logged in',
screen: DashboardScreen,
navigationOptions: {
title: 'Dashboard'
}
},
});
export default class MyApp extends Component {
//...
_signIn() {
GoogleSignin.signIn()
.then((user) => {
this.setState({
isLoggedIn: true,
user: user
})
// Redirect to screen here
})
.catch((err) => {
console.log('Signin error', err)
})
.done()
}
render() {
//...
}
}
I tried many things, I think it should be something like this:
const navigateAction = NavigationActions.navigate({
routeName: 'DashboardScreen',
params: { user: user.givenName }
})
this.props.navigation.dispatch(navigateAction)
But the console says that this.props is undefined.
Would you be able to help?
Thank you!
I am guessing this inside of _signIn is not bound to your class anymore but to the _signIn function.
To make it work you could modify _signIn into an arrow function :
_signIn = () => {
GoogleSignin.signIn()
.then((user) => {
this.setState({
isLoggedIn: true,
user: user
})
const navigateAction = NavigationActions.navigate({
routeName: 'DashboardScreen',
params: { user: user.givenName }
})
this.props.navigation.dispatch(navigateAction)
})
.catch((err) => {
console.log('Signin error', err)
})
.done()
}
Here is more info about how to bind this: https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56
You have several other articles you could look at as it is a common mistake in React.
please add myApp in stack navigator and follow this
render() {
const { navigate } = this.props.navigation;
return(
// your code
<Button onPress={() => navigate({ routeName: 'DashboardScreen' ,params: { user:userData }}) />
);
}
and you'll get the userData in DashboardScreen using this.
this.props.navigation.state.params.user
Related
I have problem, namely the navigation in this code doesn't work:
import AsyncStorage from "#react-native-async-storage/async-storage";
import createDataContext from "./createDataContext";
import trackerApi from "../api/tracker";
import { navigate } from "./navigationRef";
const authReducer = (state, action) => {
switch (action.type) {
case "add_error":
return { ...state, errorMessage: action.payload };
case "signin":
return { errorMessage: "", token: action.payload };
case "clear_error_message":
return { ...state, errorMessage: "" };
case "signout":
return { token: null, errorMessage: "" };
default:
return state;
}
};
const signup = (dispatch) => async ({ email, username, birth, gender, password }) => {
try {
const response = await trackerApi.post("/signup", { email, username, birth, gender, password });
await AsyncStorage.setItem("token", response.data.token);
dispatch({ type: "signin", payload: response.data.token });
console.log(response.data.token);
navigate("DrawerScreen");
} catch (err) {
dispatch({
type: "add_error",
payload: "Something went wrong with sign up",
});
}
};
export const { Provider, Context } = createDataContext(
authReducer,
{ signin, signout, signup, clearErrorMessage, tryLocalSignin },
{ token: null, errorMessage: "" }
);
"signup" function successfully sends my data to database in mongodb. But after this
The next file is created to help my navigation works. But "NavigationActions" was used in ReactNative v4. I need to change my code to work with RN v6. The following code is pasted below:
import { NavigationActions } from 'react-navigation';
let navigator;
export const setNavigator = nav => {
navigator = nav;
};
export const navigate = (routeName, params) => {
navigator.dispatch(
NavigationActions.navigate({
routeName,
params
})
);
};
Both files are referenced by each other.
To sum up I've tried the solution to use navigation.navigate("MyScreen"), but it doesnt work in signup function. The question is how to change the second file to work with RN6 or how to navigate successfully in this function without the second file?
First you have to import useNavigation
Like this:
import { useNavigation } from "#react-navigation/core";
Then you have to use it and save it in a variable like:
const navigation = useNavigation();
Now use onPress when press on that button to navigate:
onPress={() => navigation.navigate('MyScreen')};
This will navigate to the the other Screen.
Make sure you install every library you use in your project using npm or yarn.
You can get access to the root navigation object through a ref and pass it to the RootNavigation which we will later use to navigate.
// App.js
import { NavigationContainer } from '#react-navigation/native';
import { navigationRef } from './RootNavigation';
export default function App() {
return (
<NavigationContainer ref={navigationRef}>{/* ... */}
</NavigationContainer>
);
}
In the next step, we define RootNavigation, which is a simple module with functions that dispatch user-defined navigation actions.
// RootNavigation.js
import {createNavigationContainerRef} from '#react-navigation/native';
import {StackActions} from '#react-navigation/native';
export const navigationRef = createNavigationContainerRef();
// for navigate
export function navigate(name, params) {
if (navigationRef.isReady()) {
navigationRef.navigate(name, params);
}
}
// for replace
export function navigateReplace(name, param) {
if (navigationRef.isReady()) {
navigationRef.dispatch(
StackActions.replace(name, {
param,
}),
);
}
}
// any js module
import * as RootNavigation from './path/to/RootNavigation.js';
then you can navigate like this
RootNavigation.navigateReplace('ChatScreen', { userName: 'Lucy' });
or
RootNavigation.navigate('ChatScreen', { userName: 'Lucy' });
for more details, you can read the documentation
Navigating without the navigation prop
code
const MypageStack = createStackNavigator(
{
News,
Mypage,
},
{
initialRouteName: 'Mypage',
},
);
const postLoginNavigator = createBottomTabNavigator({
Mypage: MypageStack,
});
const AppNavigator = createStackNavigator({
Loading,
First,
PostLogin: postLoginNavigator,
}, {
mode: 'card',
headerMode: 'none',
initialRouteName: 'Loading',
defaultNavigationOptions: {
gestureEnabled: false,
},
});
const AppContainer = createAppContainer(AppNavigator);
export default class App extends React.Component {
async componentDidMount() {
Notifications.addListener(this.subscribeNotification);
}
subscribeNotification = (notification) => {
const { screen = null } = data;
// screen = 'News'
if (notification.origin === 'selected') {
if (screen) {
dispatch(NavigationActions.navigate({ routeName: screen }));
}
}
}
render() {
return (
<AppContainer />
);
}
}
What I'm trying to do
When opening notification, I want to navigate to News screen.
But, dispatch(NavigationActions.navigate({ routeName: screen })); doesn't work.
I got an error.
can't find variable: dispatch
I don't know how to navigate. I would appreciate it if you could give me any advice.
To dispatch a navigation action you have to use a root navigation object from you props like this =>
const resetAction = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'HOME_SCREEN' })] });
this.props.navigation.dispatch(resetAction);
But I found it more convenient to create a common file called NavigationService.js to manage your navigation actions, like this one
import { NavigationActions } from 'react-navigation';
let _navigator;
function setTopLevelNavigator(navigatorRef) {
_navigator = navigatorRef;
}
function navigate(routeName, params) {
_navigator.dispatch(
NavigationActions.navigate({
routeName,
params,
})
);
}
function getCurrentRoute() {
let route = _navigator.state.nav
while (route.routes) {
route = route.routes[route.index]
}
return route
}
// add other navigation functions that you need and export them
export default {
navigate,
setTopLevelNavigator,
getCurrentRoute
};
Update your <AppContainer/> to pass the navigation refs to NavigationSerive file
<AppContainer ref={ref => NavigationService.setTopLevelNavigator(ref) }
And then anywhere in your app just import and call whatever you need
import NavigationService from './NavigationService';
NavigationService.navigate(routeName, { ...yourParams })
Happy Coding
I am using wix/react-native-navigation - v1 in my react native project and I want to launch my App based on a condition as follows:
Launch App
Read credentials from storage (AsyncStorage)
If credentials found, then
Start App with Home screen
Else
Start App with Login Screen
How can I achieve this?
I have index.js
import App from './App';
App.js
...
Navigation.registerComponent("myApp.AuthScreen", () => AuthScreen);
Navigation.registerComponent("myApp.HomeScreen", () => HomeScreen);
...
// Start a App
Navigation.startSingleScreenApp({
screen: {
screen: "myApp.AuthScreen",
title: "Login"
}
});
You can have two functions that initialize single-screen apps and then call the one that fulfills the requirements.
...
Navigation.registerComponent("myApp.AuthScreen", () => AuthScreen);
Navigation.registerComponent("myApp.HomeScreen", () => HomeScreen);
...
function startHomeScreen() {
Navigation.startSingleScreenApp({
screen: {
screen: "myApp.HomeScreen",
title: "Login"
}
});
}
function startAuthScreen() {
Navigation.startSingleScreenApp({
screen: {
screen: "myApp.AuthScreen",
title: "Home"
}
});
}
function init() {
if(...) {
startAuthScreen();
} else {
startHomeScreen();
}
}
It worked! I am not sure why the app kept hanging on splashscreen. Following is the exact code:
const __init__ = () => {
try {
AsyncStorage.getItem("MY-KEY")
.then((value) => {
if (value) {
startHomeScreen();
} else {
startAuthScreen();
}
});
} catch (e) {
startAuthScreen();
}
};
__init__();
Thanks #Filip Ilievski !
Navigation.registerComponent("RootScreen", () => RootScreen);
Navigation.startSingleScreenApp({
screen: {
screen: "RootScreen",
title: "Root"
}
});
For this scenarios you can create one additional component like below.
This additional component will check your condition in async storage and decide which view to load first
import AuthScreen from './AuthScreen';
import HomeScreen from './HomeScreen';
class RootScreen {
constructor(props) {
super(props);
this.state = {
loaded: false,
screenToLoad: ''
};
}
componentDidMount() {
this.checkRoute();
}
checkRoute = () => {
AsyncStorage.getItem("MY-KEY")
.then((value) => {
this.setState({
loaded: true,
screenToLoad: value
});
});
}
renderRoute = () => {
const { screenToLoad } = this.state;
switch(screenToLoad) {
case 'AuthScreen':
return <AuthScreen />;
case 'HomeScreen':
return <HomeScreen />
default:
return null;
}
}
render () {
const { loaded } = this.state;
if (!loaded) return null;
return this.renderRoute();
}
}
I'm using the create react native app by the expo team to build an app. Using Icon component from react-native-elements to create a react navigation header feature. Snippet below:
const Navigator = new createStackNavigator({
Home: {
screen: Home,
path: '/',
navigationOptions: ({ navigation }) => ({
title: 'Home',
headerStyle: {
backgroundColor: 'black'
},
headerLeft: (
<Icon
name="menu"
size={30}
type="entypo"
style={{ paddingLeft: 10 }}
/>
),
}),
},
})
I encountered this error:
After numerous iterations, I found this supposed work around 1st and 2nd by the expo team and implemented it this way below for the app but still encountering the same problems.
import Expo from "expo";
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset, Font } from 'expo';
import { FontAwesome, Ionicons } from '#expo/vector-icons';
import { connect } from 'react-redux'
import { Auth } from 'aws-amplify';
import AuthTabs from './auth/Tabs';
import Nav from './navs/Navigator';
import Home from "./components/Home";
class App extends React.Component {
state = {
user: {},
isLoading: true,
isLoadingComplete: false,
};
async componentDidMount() {
StatusBar.setHidden(true)
try {
const user = await Auth.currentAuthenticatedUser()
this.setState({ user, isLoading: false })
} catch (err) {
this.setState({ isLoading: false })
}
}
async componentWillReceiveProps(nextProps) {
try {
const user = await Auth.currentAuthenticatedUser()
this.setState({ user })
} catch (err) {
this.setState({ user: {} })
}
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return(
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
}
else{
if (this.state.isLoading) return null
let loggedIn = false
if (this.state.user.username) {
loggedIn = true
}
if (loggedIn) {
return (
<Nav />
)
}
return (
<AuthTabs />
)
}
}
_loadResourcesAsync = async () => {
console.log("fonts loading..")
const entypoFont = {
'entypo': require('../node_modules/#expo/vector-icons/fonts/Entypo.ttf')
};
const fontAssets = cacheFonts([ FontAwesome.font, Ionicons.font, entypoFont ]);
console.log("loaded all fonts locally")
await Promise.all([...fontAssets]);
console.log("promisified all fonts")
};
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
function cacheFonts(fonts){
return fonts.map(font => Font.loadAsync(font))
}
const mapStateToProps = state => ({
auth: state.auth
})
export default connect(mapStateToProps)(App)
What are my doing wrong and how can it be configured appropriately? Thank you
I'am creating a simple application with authentication. To change a state using redux with react-native-navigation (v1). For example, index.js
...
import { Navigation, } from 'react-native-navigation';
import { Provider, } from 'react-redux';
import store from './src/store';
import registerScreens from './src/screens';
registerScreens(store, Provider);
class App {
constructor () {
this.auth = false;
store.subscribe(this.onStoreUpdate.bind(this));
this.start();
}
onStoreUpdate () {
const state = store.getState();
if (this.auth != state.auth) {
this.auth = state.auth;
this.start();
}
}
start () {
switch (this.auth) {
case false:
Navigation.startTabBasedApp({
tabs: [{
screen: 'navigation.AuthScreen',
}, {
screen: 'navigation.RegisterScreen',
},],
});
break;
case true:
Navigation.startSingleScreenApp({
screen: {
screen: 'navigation.MainScreen',
},
});
break;
}
}
}
const application = new App();
Store is listening an update and change an application layout if need.
AuthScreen show a simple ActivityIndicator, when server request is perform. For example, auth.js
...
import { bindActionCreators, } from 'redux';
import { connect, } from 'react-redux';
import * as actions from './../actions';
...
class AuthScreen extends Component {
constructor (props) {
super(props);
this.state = {
loading: false,
...
};
this.handlePressEnter = this.handlePressEnter.bind(this);
}
handlePressEnter () {
...
this.loadingState(true);
jsonFetch(url, {
method: 'POST',
body: JSON.stringify({...}),
}).then((value) => {
this.loadingState();
this.props.actions.auth(true);
}).catch((errors) => {
this.loadingState();
console.log('Error while auth', errors);
});
}
...
loadingState (state = false) {
this.setState({
loading: state,
});
}
render () {
return (<View>
...
<Modal visible={this.state.loading} transparent={true} animationType="none" onRequestClose={() => {}}>
<View>
<ActivityIndicator size="large" animating={this.state.loading} />
</View>
</Modal>
</View>);
}
}
function mapStateToProps (state, ownProps) {
return {};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps) (AuthScreen);
I'am starting application with iOS simulator and try to authenticate. It show me activity indicator, then indicator disappear, but layout does not change. And strange behavior, if I comment this.loadingState(true); and this.loadingState(); in auth.js layout changes with success.
Can someone explain to me, why layout does not change from auth to main when activity indicator using?
I think that you can use dispatch props for loading.
For example When you call this.props.actions.auth(true);
You can return loading reducers.
handlePressEnter () {
...
dispatch({ type:'loading', loading: true });
jsonFetch(url, {
method: 'POST',
body: JSON.stringify({...}),
}).then((value) => {
dispatch({ type:'loading', loading: false });
this.props.actions.auth(true);
}).catch((errors) => {
this.loadingState();
console.log('Error while auth', errors);
});
}
And than you can use
<ActivityIndicator size="large" animating={this.props.loading} />
But dont forget the reducers return