Switching from single screen to tab based app is not happening in ios, whereas same code working like charm in android
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
ImageBackground,
Dimensions,
Image,
TouchableOpacity,
Alert,
AsyncStorage
} from 'react-native';
import { strings } from '../locales/i18n';
import { Navigation } from 'react-native-navigation';
import { MKTextField } from 'react-native-material-kit';
import axios from 'axios';
import Spinner from 'react-native-loading-spinner-overlay';
import * as userActions from '../src/actions/userActions'
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Appurl from './../config';
import Validation from './../src/utils/Validation.js';
import OneSignal from 'react-native-onesignal';
class login extends Component {
constructor(props) {
super(props);
this.state = {
emailPhone : '',
visible : false,
userid : '',
password : '',
show_password: true,
}
}
static navigatorStyle = {
navBarHidden : true,
}
componentWillUnmount() {
let {actions} = this.props;
actions.toggleButton(false);
}
showPassword = () => {
let {show_password} = this.state;
if(show_password) {
this.setState({show_password:false})
}
else {
this.setState({show_password:true})
}
}
validationRules= () => {
return [
{
field: this.state.password,
name: 'Password',
rules: 'required|no_space|min:6|max:15'
},
]
}
back = () => {
let {actions} = this.props;
actions.toggleButton(false);
this.props.navigator.pop();
}
loginPassword = async() => {
let {emailPhone, visible, password} = this.state;
let validaton= Validation.validate(this.validationRules());
if(validaton.length != 0) {
return Alert.alert(
'',
validaton[0],
[
{
text: strings('globalValues.AlertOKBtn'),
onPress: ()=> {
}
}
],
{ cancelable: false }
);
}
else {
this.setState({visible : true});
let {actions} = this.props;
actions.getLoginField(emailPhone);
OneSignal.sendTag("phone", emailPhone);
let values = {'authfield' : emailPhone, 'password' : password, 'langaugeType' : this.props.user.lang};
console.log(values);
axios.post(`${Appurl.apiUrl}loginUser`, values)
.then((response) => {
console.log(response)
this.setLoginPassword(response)
}).catch((error) => {
if(error.response.data.success == 0) {
Alert.alert(
'',
error.response.data.msg,
[
{
text: strings('globalValues.AlertOKBtn'),
onPress: () => {
this.setState({visible: false});
} }
],
{ cancelable: false }
);
}
})
}
}
forgetPassword = () => {
this.props.navigator.push({
screen : 'forgotPassword'
})
}
setLoginPassword = async(response) => {
console.log(response);
let {visible} = this.state;
let {actions} = this.props;
console.log(response);
// let userid = response.data.userId;
// actions.getLoginUserId(userid);
try {
this.setState({visible: false});
let details = {'image': response.data.Profilepicurl , 'name': response.data.name , 'id': response.data.userId, 'email' : response.data.email}
await AsyncStorage.setItem('user', JSON.stringify(details));
Navigation.startTabBasedApp({
tabs: [
{
label: strings('globalValues.Tab1'),
screen: 'famcamHome',
icon: require('./../Images/ic_home_outline.png'),
selectedIcon: require('./../Images/ic_home_filled.png'), // local image asset for the tab icon selected state (optional, iOS only. On Android, Use `tabBarSelectedButtonColor` instead)
title: 'Home',
},
{
label: strings('globalValues.Tab2'),
screen: 'orders',
icon: require('./../Images/ic_clipboards_outline.png'),
selectedIcon: require('./../Images/ic_clipboards_filled.png'),
title: 'Orders',
},
{
label: strings('globalValues.Tab3'),
screen: 'profile',
icon: require('./../Images/ic_profile_outline.png'),
selectedIcon: require('./../Images/ic_profile_filled.png'),
title: 'Profile',
},
],
tabsStyle: {
tabBarButtonColor: '#C54C72',
tabBarLabelColor: '#C54C72',
tabBarSelectedButtonColor: '#C54C72',
tabBarBackgroundColor: 'white',
initialTabIndex: 0,
tabBarTextFontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null
},
appStyle: {
tabBarSelectedButtonColor: '#C54C72',
tabFontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null
},
})
}
catch(error) {}
}
render() {
let {emailPhone, password, show_password} = this.state;
return (
<View style={{flex:1, marginHorizontal: 24}}>
<Spinner visible={this.state.visible} color='#8D3F7D' tintColor='#8D3F7D' animation={'fade'} cancelable={false} textStyle={{color: '#FFF'}} />
<View style={{flex: 0.1, justifyContent: 'center'}}>
<TouchableOpacity hitSlop = {{top:7, left:7, bottom:7, right:7}} style={{height: 20, width:24, justifyContent: 'center'}} onPress={() => {this.back()}}>
<Image source={require('./../Images/icBack.png')} style={{height: 14, width:18}}/>
</TouchableOpacity>
</View>
<View style={{flex:0.09, justifyContent: 'flex-start'}}>
<Text style = {{fontSize: 24, lineHeight: 32, fontWeight: this.props.user.lang=='en'?'bold':null, color: '#000000', fontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null}}>{strings('login.login')}</Text>
</View>
<View style={{flex:0.08}}>
<Text style = {{fontSize: 14, lineHeight: 20, color: '#474D57', fontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null}}>{strings('login.heading')}</Text>
</View>
<View style = {{flex:0.15}}>
<MKTextField
placeholder = {strings('login.placeholder')}
ref="emailPhone"
placeholderTextColor='#AAAFB9'
floatingLabelEnabled
keyboardType = "email-address"
returnKeyType = "next"
textInputStyle = {{fontSize: 16, lineHeight: 24, color: '#474D57', textAlign: this.props.user.lang=='en'?'left':'right'}}
style = {{marginTop:10}}
underlineSize={1}
highlightColor='#474D57'
tintColor='#C2567A'
autoCorrect={false}
autoCapitalize= 'none'
onChangeText = {(emailPhone) => this.setState({emailPhone})}
onSubmitEditing = {(event) => {this.refs.password.focus()}}
/>
</View>
<View style = {{flex:0.12, flexDirection: 'row'}}>
<MKTextField
placeholder = {strings('login.placeholder2')}
ref="password"
placeholderTextColor='#AAAFB9'
floatingLabelEnabled
password={show_password}
keyboardType = "default"
returnKeyType = "done"
textInputStyle = {{fontSize: 16, lineHeight: 24, color: '#474D57', textAlign: this.props.user.lang=='en'?'left':'right'}}
style = {{marginTop:10, flex:0.99}}
underlineSize={1}
highlightColor='#474D57'
tintColor='#C2567A'
autoCorrect={false}
autoCapitalize= 'none'
onChangeText = {(password) => {this.setState({password})}}
/>
</View>
<View style={{flex: 0.1, justifyContent: 'flex-start'}}>
<TouchableOpacity style={{justifyContent: 'flex-start'}} onPress = {() => {this.forgetPassword()}}>
<Text style={{fontSize: 14, lineHeight: 16, color: 'black', fontWeight: this.props.user.lang=='en'?'bold':null, textAlign: this.props.user.lang=='en'?'left':'right', fontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null}}> {strings('login.forgotPassword')} </Text>
</TouchableOpacity>
</View>
<View style={{flex: 0.1, justifyContent: 'center'}}>
<Text style={{fontSize: 13, color: '#9B9B9B', lineHeight: 16, textAlign: 'center', fontFamily: this.props.user.lang=='ar'?'AssawtDecorative':null}}>* {strings('login.NotificationText')}</Text>
</View>
<View style = {{flex:0.1,alignItems : 'flex-end'}}>
<TouchableOpacity activeOpacity={0.5} onPress = {() => {this.loginPassword()}}>
<Image source = {require('./../Images/fab.png')} style={{height: 56, width: 56}} />
</TouchableOpacity>
</View>
</View>
)
}
}
function mapStateToProps(state, ownProps) {
return {
user: state.user
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(userActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(login);
It is working perfect in Android, but in ios if i directly call starttabbased onPress it works but like i used in the code the screen does not change, but the logs did of tabs
@guyca any help on this??
Same issue is happening with lightbox on ios, after executing axios request it does not close, and if i comment the axios part and call directly onPress it works
i've tried every way but nothing working, please help
@guyca
Any help guys, i'm really stuck on this
@guyca @dvxme @DanielZlotin @talkol @gran33
Hey @rf1804
I'm not aware of any issues with calling startTabBasedApp
after startSingleScreenApp
. I see you wrapped the call with try catch, is an error thrown? Did you try printing it?
did you check the native logs? Did you try to debug the native iOS code?
I really can't help you much here.
@guyca there are no errors, i've included the screenshots of logs, there are no errors, the thing is everything happens correctly but the screen does not get changed, even after the action no buttons functionality works, it's like everything happened but nothing showing and this problem is happening in ios only in android working fine
+1 same issue here
while i was playing it around to find some solution i found out the problem got solved if i do not show the spinner, i'm thinking setting state for spinner interrupts the screen rendering only in ios case of tab based app
A testflight user is facing this (the app is not changing from the single screen to the tabbed screen).
I am not able to reproduce.
The logs show that the tab screens loaded, but then all of a sudden it we back to the single screen one.
@rf1804 do you have a repo with this issue isolated for easy reproduction?
I was FINALLY able to fix the issue after a long time debugging.
The problem and solution were very similar to @rf1804's.
Avoid making UI changes in the current screen at the same moment you are transitioning to another root app.
I had this scenario:
The issue was because the LOGIN_SUCCESS was not only dispatching the root app change, but it was causing a lot of UI updates, specially inside the login phone modal. This was interrupting the root change.
So it was fixed by preventing these UI changes. In my case, as it was the phone modal that were making UI changes, I had to make sure it was completely closed by calling await navigator.dismissModal()
(the await
is essential) before dispatching LOGIN_SUCCESS.
PS: The screens from the tab based app were created, their componentDidMount were called, etc, it just wasn't becoming visible. The previous single screen app wasn't destroyed / stayed on top of it.
I do think this is a bug though, just a possibly rare one.
The root change should always work, nothing should be able to interrupt it.
_Or: the previous root app should always be destroyed, nothing should keep it mounted/visible after a root change_
Maybe this should be reopened? cc @guyca
@brunolemos yes the problem is if ui changes occur, previous root app does not get destroyed and it causes the problem, @guyca may be it can be handled for future uplifting of wix navigation.
Interesting.. @guyca try to reproduce this in e2e in v2?
Agree it needs to be fixed. For me I use rn-modal to show a loading indication as a overlay across the app. And most of the time it will show up before transition, right now I have to rewrite all of them to solve this 馃槩
We're also seeing this issue of the root app not being changed. I'll see if I can put together a basic simple app to replicate.
Most helpful comment
I was FINALLY able to fix the issue after a long time debugging.
The problem and solution were very similar to @rf1804's.
TL/DR
Avoid making UI changes in the current screen at the same moment you are transitioning to another root app.
Details
I had this scenario:
The issue was because the LOGIN_SUCCESS was not only dispatching the root app change, but it was causing a lot of UI updates, specially inside the login phone modal. This was interrupting the root change.
So it was fixed by preventing these UI changes. In my case, as it was the phone modal that were making UI changes, I had to make sure it was completely closed by calling
await navigator.dismissModal()
(theawait
is essential) before dispatching LOGIN_SUCCESS.