I'm doing wrong with the navigation, I use SwitchNavigator to handle the auth and app navigation
After performing login I try to redirect user to the AppNavigation but Im got this error :
undefined is not an object (evaluating 'this.props.navigation')
Navigation.js
import { createSwitchNavigator, createAppContainer } from 'react-navigation'
import AuthNavigation from './AuthNavigation'
import AppNavigation from './AppNavigation'
const SwitchNavigator = createSwitchNavigator(
{
Auth: AuthNavigation,
App: AppNavigation
},
{
initialRouteName: 'Auth'
}
)
const AppContainer = createAppContainer(SwitchNavigator)
export default AppContainer
LoginScreen.js
import React, { memo, useState } from 'react';
import { TouchableOpacity, StyleSheet, Text, View } from 'react-native';
import Background from '../Components/Background';
import Logo from '../Components/Logo';
import Header from '../Components/Header';
import Button from '../Components/Button';
import TextInput from '../Components/TextInput';
import BackButton from '../Components/BackButton';
import { theme } from '../CustomProperties/Themes';
import { emailValidator, passwordValidator } from '../CustomProperties/utils';
import Login from '../Components/Login';
const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState({ value: '', error: '' });
const [password, setPassword] = useState({ value: '', error: '' });
const _onLoginPressed = () => {
const emailError = emailValidator(email.value);
const passwordError = passwordValidator(password.value);
if (emailError || passwordError) {
setEmail({ ...email, error: emailError });
setPassword({ ...password, error: passwordError });
return;
}
else {
const login = new Login();
login.realmLogin(email.value, password.value);
}
};
return (
<Background>
<BackButton goBack={() => navigation.navigate('HomeScreen')} />
<Logo />
<Header>Welcome back.</Header>
<TextInput
label="Email"
returnKeyType="next"
value={email.value}
onChangeText={text => setEmail({ value: text, error: '' })}
error={!!email.error}
errorText={email.error}
autoCapitalize="none"
autoCompleteType="email"
textContentType="emailAddress"
keyboardType="email-address"
/>
<TextInput
label="Password"
returnKeyType="done"
value={password.value}
onChangeText={text => setPassword({ value: text, error: '' })}
error={!!password.error}
errorText={password.error}
secureTextEntry
/>
<View style={styles.forgotPassword}>
<TouchableOpacity
onPress={() => navigation.navigate('ForgotPasswordScreen')}
>
<Text style={styles.label}>Forgot your password?</Text>
</TouchableOpacity>
</View>
<Button mode="contained" onPress={_onLoginPressed}>
Login
</Button>
<View style={styles.row}>
<Text style={styles.label}>Don’t have an account? </Text>
<TouchableOpacity onPress={() => navigation.navigate('RegisterScreen')}>
<Text style={styles.link}>Sign up</Text>
</TouchableOpacity>
</View>
</Background>
);
};
const styles = StyleSheet.create({
forgotPassword: {
width: '100%',
alignItems: 'flex-end',
marginBottom: 24,
},
row: {
flexDirection: 'row',
marginTop: 4,
},
link: {
fontWeight: 'bold',
},
});
export default memo(LoginScreen);
Login.js
import React, { Component } from 'react';
import {
Alert,
Button,
KeyboardAvoidingView,
Image,
StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import Auth0 from 'react-native-auth0';
var credentials = require('../auth0-credentials');
const auth0 = new Auth0(credentials);
export default class Login {
constructor(props) {
//super(props);
this.state = { viewLogin: true };
this.realmLogin = this.realmLogin.bind(this);
this.createUser = this.createUser.bind(this);
}
navigateHomeApp() {
this.props.navigation.navigate("App");
};
onSuccess(credentials) {
auth0.auth
.userInfo({ token: credentials.accessToken })
.then(profile => {
//console.log(credentials);
//console.log(profile);
this.props.onAuth(credentials, profile);
})
.catch(console.error);
}
alert(title, message) {
Alert.alert(
title,
message,
[{ text: 'OK', onPress: () => console.log('OK Pressed') }],
{ cancelable: false }
);
}
realmLogin(username, password) {
auth0.auth
.passwordRealm({
username: username,
password: password,
realm: 'Username-Password-Authentication',
scope: 'openid profile email',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
this.navigateHomeApp();
})
.catch(console.error);
}
createUser(username, password) {
auth0.auth
.createUser({
email: username,
password: password,
connection: 'Username-Password-Authentication',
})
.then(success => {
console.log(success)
this.alert('Success', 'New user created')
//navigation.navigate('LoginScreen');
})
.catch(error => {
this.alert('Error', error.json.description)
});
}
webAuth(connection) {
auth0.webAuth
.authorize({
scope: 'openid profile email',
connection: connection,
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
})
.catch(error => this.alert('Error', error.error_description));
};
}
Sound like I can't access the navigation object who was first declare on the LoginScreen
const LoginScreen = ({ navigation })
Any help would be appreciate
You are calling this.props.navigation.navigate("App") in Login.js class which is just utility class. Navigation works in component
LoginScreen.js
const onNavigateCallback = () => {
this.props.navigation.navigate("App");
}
const _onLoginPressed = () => {
........
if (emailError || passwordError) {
......
}
else {
const login = new Login();
login.realmLogin(email.value, password.value, onNavigateCallback); // Add this here
}
};
Login.js
realmLogin(username, password, callback = undefined) {
auth0.auth
.passwordRealm({
username: username,
password: password,
realm: 'Username-Password-Authentication',
scope: 'openid profile email',
audience: 'https://' + credentials.domain + '/userinfo'
})
.then(credentials => {
this.onSuccess(credentials);
// this.navigateHomeApp(); // remove this from here
if(callback) callback(); // Add this here
})
.catch(console.error);
}
try
navigateHomeApp =()=> {
this.props.navigation.navigate("App");
};
Related
I am building my first React Native app. I am in the process of making the automatic login with AsyncStorage. I already got the login page figured out and I managed to store the user in AsyncStorage. But in the
App.js
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import Login from './components/login'
import Signup from './components/signup'
import Home from './components/home'
import Profile from './components/profile';
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const c = async () => {
const ch = await getData('#userauthkey')
return ch
}
console.log(c())
const AppNavigator = createStackNavigator({
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const AppNavigator2 = createStackNavigator({
Home: {screen: Home},
Login: {screen: Login},
Signup: {screen: Signup},
Home: {screen: Home},
Profile: {screen: Profile},
})
const App = createAppContainer(c() == false ? AppNavigator : AppNavigator2)
export default App;
I get "Error: Objects are not valid as a React child (found: object with keys {_U, _V, _W, _X}). If you meant to render a collection of children, use an array instead." How can I fix it to make it sign the user in automatically?
login screen
import { StatusBar } from 'expo-status-bar'
import React, { useState, useEffect } from 'react'
import { TextInput } from 'react-native-gesture-handler';
import AsyncStorage from '#react-native-async-storage/async-storage';
import { StyleSheet, Text, View, FlatList, Image, Button, Pressable, ScrollView } from 'react-native';
export default function Login(props) {
const message = props.navigation.getParam('message', null)
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const setStringValue = async (value, user) => {
try {
await AsyncStorage.setItem(user, value)
} catch (e) {
console.log(e)
}
}
const getData = async (user) => {
try {
const value = await AsyncStorage.getItem('#userauthkey')
if (value !== null) {
return value
} else {
return false
}
} catch (e) {
console.log(e)
}
}
const log = () => {
fetch(`http://192.168.5.223:8000/home/login/`, {
method: 'POST',
headers: {
"Content-Type": 'application/json'
},
body: JSON.stringify({ username: username, password: password }),
})
.then(res => res.json())
.then(async (res) => {
console.log(res)
if (res.valid === true) {
await setStringValue(username, '#userauthkey')
let ch = await getData(username)
console.log(ch)
if (res.set === true) {
props.navigation.navigate("Home", { "user": username })
} else {
props.navigation.navigate("Profile", { "user": username })
}
} else {
props.navigation.navigate("Login", { 'message': "username/password are incorrect" })
}
})
.catch(error => console.log(error))
}
const sign = () => {
props.navigation.navigate("Signup")
}
return (
<View style={styles.container} >
<ScrollView style={styles.scroll} >
<View style={styles.main}>
<Text style={styles.error}> {message} </Text>
< Text style={styles.label} >
Username:
</Text>
<TextInput style={styles.input} placeholder="Username"
onChangeText={text => setUsername(text)} value={username}
autoCapitalize={'none'}
/>
<Text style={styles.label}> Password: </Text>
<TextInput style={styles.input} placeholder="Password" onChangeText={text => setPassword(text)}
value={password} secureTextEntry={true} autoCapitalize={'none'}
/>
<Button onPress={() => log()} title="Login" > </Button>
</View>
</ScrollView>
< View style={styles.footer} >
<Button onPress={() => sign()} title="Don't have an acount? Sign up now" />
</View>
< StatusBar style="auto" />
</View>
)
}
Login.navigationOptions = screenProps => ({
headerLeft: () => null,
gestureEnabled: false,
headerStyle: {
backgroundColor: 'black'
},
headerTintColor: 'white',
})
const styles = StyleSheet.create({
error: {
textAlign: 'center',
color: 'red',
},
main: {
paddingTop: 145,
paddingHorizontal: 5,
},
container: {
flex: 1,
backgroundColor: 'black',
},
scroll: {
backgroundColor:'#'
},
footer: {
borderTopColor: '#fff',
padding:35,
},
label: {
fontSize: 24,
color: "white",
padding: 10,
},
input: {
fontSize: 24,
backgroundColor: "white",
padding: 10,
margin: 10,
borderRadius: 5,
},
});
Did you imported AsyncStorage in your app.js?
import AsyncStorage from '#react-native-async-storage/async-storage';
You used it but didn't import it, in the code shown. Try to import it and recompile
I have managed to get my login authentification working, but I am having an issue with the conditional navigator in App.js. If I log in using the LoginStackNavigator, I then need to refresh the app to be able to use the DrawerNavigator. I would like it so that as soon as I log in, App.js realises this, and takes me to the drawer navigator.
I've tried to use the Context API but it isn't working. What am I doing wrong?
Here is my AppContext.js
import { createContext } from "react";
const AppContext = createContext({
isloggedIn: {},
setLoggedIn: () => {},
});
export default AppContext;
Here is my App.js:
import AppContext from "./src/Component/AppContext";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
isloggedIn: false,
};
this.loginStatusCheck();
}
loginStatusCheck = async () => {
const userToken = await AsyncStorage.getItem("#storage_Key");
if (userToken) {
this.setState({ isloggedIn: true });
} else {
this.setState({ isloggedIn: false });
}
};
render() {
return (
<AppContext.Provider
value={{
isloggedIn: this.state.isloggedIn,
setLoggedIn: this.setLoggedIn,
}}
>
<NavigationContainer>
{this.state.isloggedIn ? (
<DrawerNavigator />
) : (
<LoginStackNavigator />
)}
</NavigationContainer>
</AppContext.Provider>
);
}
}
And here is my LoginScreen.js:
import AppContext from "../../Component/AppContext";
const LoginCall = () => {
const { setLoggedIn } = useContext(AppContext);
return setLoggedIn(true);
};
export default class LoginScreen extends Component {
login = async () => {
const { email, password, confirmpassword } = this.state;
console.log(email);
axios
.post("http://127.0.0.1:8002/rest-auth/login/", {
username: "username",
email: "default#email.com",
password: "password",
})
.then((response) => {
console.log(response.data.key);
this.storeKey(response.data.key);
LoginCall;
})
//.then(this.props.navigation.navigate("HomeScreen"))
.catch((error) => {
console.log(error);
});
};
storeKey = async (value) => {
try {
await AsyncStorage.setItem("#storage_Key", value);
} catch (e) {
console.log("error" + e);
} finally {
console.log("done");
}
};
render() {
return (
<View
style={{
backgroundColor: "#fff",
paddingTop: 40,
alignItems: "center",
flex: 1,
}}
>
<TouchableOpacity onPress={() => this.login()}>
<Text>Login</Text>
</TouchableOpacity>
</View>
);
}
}
I think its because you're not importing axios on LoginSreen.
Try to add import axios from 'axios' with others importations
I'm new to react native concepts. I'm trying to pass functions and a parameter to context API so that I can access those in my child component. I'm trying to implement basic login functionality that will show different messages based on user login status. The functionality works when I pass the SignIn method to my child component but the same function is not accessible when I send it along with some variable. The below code and notes can explain the problem clearly.
In the below code as you can see I'm passing my reducer function and initial states from which I get the error messages and sign function <AuthenticationContext.Provider value={[authContext, initialLoginState]}>
App.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import BottomNavigation from './src/Navigations/BottomNavigation';
import AuthStackNavigation from './src/Navigations/AuthStackNavigation'
import { useEffect, useMemo, useReducer } from 'react';
import { View } from 'react-native-animatable';
import apiCaller from "./src/api/apiCaller";
import { ActivityIndicator, Text } from 'react-native';
import { AuthenticationContext } from './src/context/AuthenticationContext'
import { Provider as VideoProvider } from './src/context/videoContext'
import { Context as VideoContext } from './src/context/videoContext'
import AsyncStorage from '#react-native-async-storage/async-storage'
export default function App() {
//Initial state values
const initialLoginState = {
isLoading: true,
userName: null,
userToken: null,
errorMessage: ''
}
//Reducer function
const loginReducer = (prevState, action) => {
switch (action.type) {
case 'LOGIN':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false
};
case 'LOGOUT':
return {
...prevState,
userName: null,
userToken: null,
isLoading: false,
};
case 'REGISTER':
return {
...prevState,
userToken: action.token,
userName: action.id,
isLoading: false,
};
case 'ERROR':
return {
...prevState,
errorMessage: action.response,
isLoading: false
};
default:
return { userToken: null }
}
}
//Defining useReducer
const [newLoginState, dispatch] = useReducer(loginReducer, initialLoginState);
const authContext = useMemo(() => ({
signIn: async (email, password) => {
try {
const userData = {
email: email,
password: password,
};
const response = await apiCaller.post("/login", userData);
if (response.data.code == '200') {
await AsyncStorage.setItem('userToken', response.data.token)
dispatch({ type: 'LOGIN', id: email, token: response.data.token })
}
else if (response.data.code == '404') {
dispatch({ type: 'ERROR', response: response.data.message })
}
} catch (err) {
console.log(err);
}
}
}), []);
return (
<VideoProvider value={VideoContext}>
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
<NavigationContainer>
{newLoginState.userToken == null ?
<AuthStackNavigation />
:
<BottomNavigation />
}
</NavigationContainer>
</AuthenticationContext.Provider>
</VideoProvider>
);
}
and in the below file, I'm fetching the provider vales by
const { signIn, initialLoginState } = useContext(AuthenticationContext) but its giving me "signIn is not a function. (In 'signIn(email, password)', 'signIn' is undefined)" error but signIn method is accessible when i just try to pass and access SignIn method alone.
Signinscreen.js
import React, { useState } from 'react'
import { Ionicons, MaterialIcons } from '#expo/vector-icons';
import { Text, View, StyleSheet, TouchableOpacity, Platform, StatusBar } from 'react-native';
import { WindowHeight, WindowWidth } from '../utils/PlatformDimention'
import PrimaryFormInput from "../components/PrimaryFormInput";
import PrimaryFormButton from "../components/PrimaryFormButton";
import { AuthenticationContext } from "../context/AuthenticationContext";
import { useContext } from 'react';
const UserLogin = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { signIn, initialLoginState } = useContext(AuthenticationContext)
const loginHandle = (email, password) => {
signIn(email, password);
}
return (
<View style={styles.container}>
<StatusBar barStyle='light-content' />
<View style={styles.header}>
<View style={{ flex: 1, }}>
<TouchableOpacity onPress={() => navigation.goBack()}>
<MaterialIcons style={styles.goBack} name="arrow-back" size={WindowHeight / 27} />
</TouchableOpacity>
</View>
<View style={{ flex: 2, alignItems: 'center', justifyContent: 'center', }}>
<Ionicons
style={{ color: "#fff" }} name="logo-bitbucket" size={WindowHeight * 10 / 100} />
</View>
<View style={{ flex: 1, alignItems: 'flex-end' }}>
</View>
</View>
<View style={styles.footer}>
<View style={styles.topFlex}></View>
<View style={styles.middleFlex}>
<Text style={styles.loginText}>Welcome!</Text>
<Text style={styles.loginSubTextSub}>Login to your existing account.</Text>
<PrimaryFormInput
inputValue={email}
onChangeText={(userEmail) => setEmail(userEmail)}
inputPlaceHolder='Email'
iconName="user"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<PrimaryFormInput
inputValue={password}
onChangeText={(userPassword) => setPassword(userPassword)}
inputPlaceHolder='Password'
iconName="key"
secureTextEntry={true}
/>
<PrimaryFormButton
ButtonText='Login'
onPress={() => loginHandle(email, password)}
/>
</View>
<View style={styles.bottomFlex}></View>
</View>
</View>
)
}
export default UserLogin
Could anyone of you please let me know how to pass both method and parameters to my child component and access it?
Your mistake is just here
const { signIn, initialLoginState } = useContext(AuthenticationContext)
Because you pass the context object like this
<AuthenticationContext.Provider value={{authContext, initialLoginState}}>
You need to do the destructruring like this
const { authContext : { signIn }, initialLoginState } = useContext(AuthenticationContext)
I am new to Rematch for Redux. I am redoing my application using Rematch, but I do not know if I am doing any of this correctly? How/Where do I trigger my reducers and effects (Step 3 in Rematch Docs)? I cannot see my state when I switch between screens. The state seems to be resetting. Could someone please help me? I would greatly appreciate it.
I followed this: https://github.com/rematch/rematch
Here is my code down below:
My application will get bigger, thus the reason why I placed these in different files to build up into the models.js.
store/user.js
const user = {
state: {},
reducers: {
login(payload) { return payload },
email(state, payload) {
return { ...state, payload }
},
password(state, payload) {
return { ...state, payload }
},
username(state, payload) {
return { ...state, payload }
},
fullname(state, payload) {
return { ...state, payload }
},
}
}
export default user
store/index.js
import { init } from '#rematch/core'
import user from './user'
const models = {
user,
}
const store = init({
models,
})
export default store
App.js
import * as React from 'react';
import SwitchNavigator from './navigation/switchNavigator'
import { Provider } from 'react-redux'
import 'redux'
import store from './store/index'
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<SwitchNavigator/>
</Provider>
);
}
}
navigation/switchNavigator
import * as React from 'react';
import TabNavigatorScreen from './tabNavigator'
import AuthNavigatorScreen from './authNavigator'
import { createAppContainer } from 'react-navigation';
import { createSwitchNavigator } from 'react-navigation-switch-transitioner'
const switchNavigator = createSwitchNavigator(
{
Home: {
screen: TabNavigatorScreen
},
Auth: {
screen: AuthNavigatorScreen
}
},
{
initialRouteName: 'Auth',
}
);
export default createAppContainer(switchNavigator);
navigation/authNavigator
import * as React from 'react';
import LoginScreen from '../screens/login'
import SignUpScreen from '../screens/signup'
import MainScreen from '../screens/main'
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
const authNavigator = createStackNavigator(
{
Main: {
screen: MainScreen,
navigationOptions: {
header: null,
}
},
Login: {
screen: LoginScreen,
navigationOptions: {
title: 'Login',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
},
Signup: {
screen: SignUpScreen,
navigationOptions: {
title: 'Sign Up',
headerTintColor: '#404040',
headerTitleStyle: {
color: '#404040',
},
headerBackTitleStyle: {
color: '#404040',
},
headerBackTitle: null,
}
}
},
{
initialRouteName: 'Main',
}
);
export default createAppContainer(authNavigator);
screens/signUp
import * as React from 'react';
import {
TextInput,
Text,
KeyboardAvoidingView,
SafeAreaView,
TouchableOpacity,
Alert,
}
from 'react-native';
import styles from '../styles'
import { connect } from 'react-redux';
import '#expo/vector-icons';
import 'redux';
class Signup extends React.Component {
render() {
const { routeName } = this.props.navigation.state
return (
<SafeAreaView style={styles.container}>
<KeyboardAvoidingView behavior='position'>
<Text style={styles.mainText}>
EMAIL
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.email}
onChangeText={input => this.props.email(input)}
/>
<Text style={styles.mainText}>
PASSWORD
</Text>
<TextInput
style={styles.inputText}
editable={routeName === 'Signup' ? true : false}
value={this.props.user.password}
onChangeText={input => this.props.password(input)}
secureTextEntry={true}
/>
<Text style={styles.mainText}>
USERNAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.username}
onChangeText={input => this.props.username(input)}
/>
<Text style={styles.mainText}>
FULL NAME
</Text>
<TextInput
style={styles.inputText}
value={this.props.user.fullname}
onChangeText={input => this.props.fullName(input)}
/>
<TouchableOpacity
style={styles.buttonLighGray}
onPress={() => Alert.alert('Sign up')}>
<Text style={styles.buttonDarkText}>
Accept & Sign Up
</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
</SafeAreaView>
);
}
}
const mapState = (state) => ({
user: state.user,
})
const mapDispatch = ({ user: { email, password, username, fullname }}) => ({
email: () => email(),
password: () => password(),
username: () => username(),
fullname: () => fullname(),
})
export default connect(mapState, mapDispatch)(Signup)
The Login screen is using the same state as Sign up screen.
The state will never change because you don't pass any parameters while dispatching an action.
const mapDispatch = dispatch => ({
setEmail: mail => dispatch.user.email(mail)
})
Everytime you called a function before you just invoked the reducer without a parameter.
Regarding your rematch model. Your reducers should not just return ...state & payload. Try to
const user = {
state: {},
reducers: {
email(state, email) {
return { ...state, email }
},
password(state, password) {
return { ...state, password }
},
username(state, username) {
return { ...state, username }
},
fullname(state, fullname) {
return { ...state, fullname }
},
}
}
Application resets to initial screen after form submitting (redux-form or Formik). The Login screen is the second screen in initial route (first is the Start screen). After submitting to occur a call dispatch and return new state -> { ...state, ...{ isLoading: true } } and on this step my app was resets.
What am I doing wrong? How to make the form work without a reset?
LoginScreen.js
import React, { Component } from 'react'
import { View, Text, Button } from 'react-native'
import { connect } from 'react-redux'
import { LoginForm } from '../../forms'
import { userLogin } from '../../store/modules/account'
import s from './styles'
class LoginScreen extends Component {
onLogin = (values, bag) => {
const { userLogin } = this.props
const { email, password } = values
userLogin({ email, password })
}
render() {
const { account, navigation } = this.props
return (
<View style={s.wrapper}>
<View style={s.mainContainer}>
<Text>This is SignIiiiiiin!</Text>
<LoginForm
handleLogin={this.onLogin}
/>
</View>
</View>
)
}
}
const mapStateToProps = ({ account }) => ({
account,
})
export default connect(mapStateToProps, {
userLogin
})(LoginScreen)
LoginForm.js
import React, { Component } from 'react'
import { View, TouchableOpacity, Button } from 'react-native'
import { Formik } from 'formik'
import { FieldInput } from '../../components'
import s from './styles'
class LoginForm extends Component {
render() {
const {
handleLogin,
} = this.props
return (
<View style={s.wrapper}>
<Formik
initialValues={{ email: '', password: '', confirmPassword: '' }}
onSubmit={handleLogin}
render={({
values,
handleSubmit,
setFieldValue,
errors,
touched,
setFieldTouched,
isValid,
isSubmitting,
}) => (
<React.Fragment>
<FieldInput
label="Email"
autoCapitalize="none"
value={values.email}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="email"
error={touched.email && errors.email}
/>
<FieldInput
label="Password"
autoCapitalize="none"
secureTextEntry
value={values.password}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="password"
error={touched.password && errors.password}
/>
<FieldInput
label="Confirm Password"
autoCapitalize="none"
secureTextEntry
value={values.confirmPassword}
onChange={setFieldValue}
onTouch={setFieldTouched}
name="confirmPassword"
error={touched.confirmPassword && errors.confirmPassword}
/>
<Button
backgroundColor="blue"
title="Submit"
onPress={handleSubmit}
disabled={!isValid || isSubmitting}
loading={isSubmitting}
/>
</React.Fragment>
)}
/>
</View>
)
}
}
export default LoginForm
store/index.js
import { applyMiddleware, compose, createStore } from 'redux'
import thunk from 'redux-thunk'
import { persistStore, persistReducer } from 'redux-persist'
import logger from 'redux-logger'
import storage from 'redux-persist/lib/storage'
import makeRootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
blacklist: ['account']
}
const persistedReducer = persistReducer(persistConfig, makeRootReducer)
const enhancer = compose(applyMiddleware(thunk, logger))
export default function configureStore() {
const store = createStore(persistedReducer, enhancer)
const persistor = persistStore(store)
return { store, persistor }
}
store/modules/account.js
import { AsyncStorage } from 'react-native'
import { cloneDeep, assignIn, merge } from 'lodash'
import axios from 'axios'
import moment from 'moment'
import CNST from '../constants'
import { ENV } from '../../config'
// ------------------------------------
// Actions
// ------------------------------------
export function userLogin(data = {}) {
const username = data.email
const password = data.password
return (dispatch) => {
dispatch({
type: CNST.ACCOUNT.LOGIN.LOADING
})
/*
axios.post(`${ENV.ROOT_URL}/user/login`, { username, password })
.then((response) => {
const { token } = response.data
const validFrom = moment().format()
dispatch({
type: CNST.ACCOUNT.LOGIN.SUCCESS,
response: { token, username, validFrom }
})
return response
})
.catch((error) => {
console.log('error', error.response)
dispatch({
type: CNST.ACCOUNT.LOGIN.FAILED,
response: error
})
})
*/
}
}
// ------------------------------------
// Reducers
// ------------------------------------
const initialState = {
id: '',
email: '',
username: '',
token: '',
error: {},
isLoading: false
}
export default function accountReducer(state = cloneDeep(initialState), action) {
switch (action.type) {
case CNST.ACCOUNT.LOGIN.SUCCESS: {
// console.log('action', action)
const { token, username, validFrom } = action.response
const _items = [
['token', token || ''],
['username', username || ''],
['validFrom', validFrom || ''],
]
AsyncStorage.multiSet(_items)
return { ...state, ...{ error: {}, token, username, email: username, isLoading: false } }
}
case CNST.ACCOUNT.LOGIN.FAILED:
return { ...state, ...{ error: action.response, isLoading: false } }
case CNST.ACCOUNT.LOGIN.LOADING:
return { ...state, ...{ isLoading: true } }
default:
return state
}
}
store/constants/account.js
export default {
LOGIN: {
LOADING: 'LOGIN',
SUCCESS: 'LOGIN_SUCCESS',
FAILED: 'LOGIN_FAILED'
}
}
store/reducers.js
import { combineReducers } from 'redux'
import account from './modules/account'
export default combineReducers({
account,
})
I fixed this issue. The problem was in the main container where I mounting Navigator and pass state for changing routes between different Stacks. I passed all state object and when one of object`s item was changed Navigator resetting navigation states.
. . .
return (
<View style={{ flex: 1 }}>
<Navigator
onNavigationStateChange={() => Keyboard.dismiss()}
/>
</View>
)
}
}
const mapStateToProps = ({ account }) => ({
account // <-- bad idea
token: account.token // <-- good one
})
export default connect(mapStateToProps, {
getSomeData
})(MainContainer)