How to set dashboard as first screen after login in react-native - react-native

I am using react-native where my first screen is Welcome screen and I want to set dashboard on my first screen when the user is login.
Here is my code:
componentWillMount(){
var self = this;
AsyncStorage.getItem(AppStrings.contracts.IS_LOGGED_IN).then((json) =>{
try{
var userDetail = JSON.parse(json);
if(userDetail.isLoggedIn != undefined && userDetail.isLoggedIn == true){
Actions.dashboard();
}
}catch(e){
}
})
}
I set this code on the Welcome screen and its working fine in IOS. But in android issue is it shows the Welcome screen for 5 to 10 seconds before going to dashboard screen when the user is login.
Here I am using react-native-router-flux for the navigation bar.

Because AsyncStorage.getItem() is asynchronous, your render() function is being called BEFORE it has been fulfilled.
So the flow of your application is:
Call componentWillMount()
AsyncStorage.getItem()
render() - This is where you see your Welcome Screen for 5-10 seconds
AsyncStorage has been fulfilled - .then() and then the User gets redirected to the dashboard.
I would set an isLoaded flag in your state:
constructor() {
super();
this.state = {
isLoaded: false,
}
}
Then inside of your componentWillMount() function, set the value to true once AsyncStorage has fulfilled its Promise.
try {
var userDetail = JSON.parse(json);
if(userDetail.isLoggedIn != undefined && userDetail.isLoggedIn == true){
Actions.dashboard();
}
this.setState({ isLoaded: true });
}
And finally, I would add some sort of loading indicator inside of render() to show the User that your application is still performing some logic.
render() {
if(this.state.isLoading) {
return <Text>I am loading</Text>
} else {
return ...
}
}

Related

Not understand on behavior

Currently, I am learning react native and still don't know why it happened.
I have a component A and component B.
In component A when handle success I will pass isSucceeded: true and false for the failure case.
but I don't know why when having success case it navigates to B screen and always show Fail text before showing Success text for 1 second
component A:
login() {
dataService.getService().then((response) => {
navigateService.navigate('B', {isSucceeded: true})
}).catch(error => {
navigateService.navigate('B', {isSucceeded: false})
});
}
In component B:
componentDidMount() {
this.state.isSucceeded = navigation.getParam('isSucceeded')
}
render() {
this.state.isSucceeded ? <View> <Text>Success</Text></View> :
<View> <Text>Fail</Text></View>
}
How can it show Success Text when having success case and fail for failed case
Thanks you.
You should do it in the constructor
constructor(props) {
super(props);
this.state = {
isSucceeded: props.navigation.getParam('isSucceeded'),
};
}
If you insist doing it in componentDidMount then you must use setState():
componentDidMount() {
const isSucceeded = this.props.navigation.getParam('isSucceeded');
this.setState({ isSucceeded });
}

React Native: How to check login status?

I am using react native router flex for navigation and I have 2 scene A and B,also using redux for storing state.
if user login status is true the app will redirect to Page B else Page A.
But I am unable to check the status.componentDidMount always getting false state.
Page A:
componentDidMount () {
if(status)
{
Actions.b();
}
}
const mapStateToProps = ({userData}) => {
const {
status,
} = userData;
return {
status,
};
}
Let me know how to check login status.
Try to use componentWillReceiveProps. As soon as your status changes, your component will route to the next screen.
componentWillReceiveProps(nextProps) {
if(nextProps.status) {
Actions.b();
}
}

How to avoid navigating to other screen multiple times

When press on any button on my React Native App to navigate to a different screen multiple times, then it will redirected to the next screen multiple times.
My sample code is:
// This is my button click event
myMethod()
{
this.props.navigation.navigate("ScreenName")
}
I am using react-navigation to navigate through my app.
How can I fix this behaviour?
I think there are a few ways this could be done. Perhaps recording when the navigation has occurred and preventing it from navigating multiple times.
You may also want to consider resetting hasNavigated after an amount of time etc as well.
// Somewhere outside of the myMethod scope
let hasNavigated = false
// This is my button click event
myMethod()
{
if (!hasNavigated) {
this.props.navigation.navigate("ScreenName")
hasNavigated = true
}
}
This react-navigation issue contains a discussion about this very topic, where two solutions were proposed.
The first, is to use a debouncing function such as Lodash's debounce that would prevent the navigation from happening more than once in a given time.
The second approach, which is the one I used, is to check on a navigation action, whether it is trying to navigate to the same route with the same params, and if so to drop it.
However, the second approach can only be done if you're handling the state of the navigation yourself, for example by using something like Redux.
Also see: Redux integration.
One of solution is custom custom components with adds debounce to onPress:
class DebounceTouchableOpacity extends Component {
constructor(props) {
super(props);
this.debounce = false;
}
_onPress = () => {
if (typeof this.props.onPress !== "function" || this.debounce)
return;
this.debounce = true;
this.props.onPress();
this.timeoutId = setTimeout(() => {
this.debounce = false;
}, 2000);
};
componentWillUnmount() {
this.timeoutId && clearTimeout(this.timeoutId)
}
render() {
const {children, onPress, ...rest} = this.props;
return (
<TouchableOpacity {...rest} onPress={this._onPress}>
{children}
</TouchableOpacity>
);
}
}
another: wrap onPress function into wrapper with similar behavior
const debounceOnPress = (onPress, time) => {
let skipCall = false;
return (...args) => {
if (skipCall) {
return
} else {
skipCall = true;
setTimeout(() => {
skipCall = false;
}, time)
onPress(...args)
}
}
}

In React Native, the method of BackHandler does not work until back root route

when I press physical return button on my phone the log have no output until back root route
componentWillMount(){
BackHandler.addEventListener('hardwareBackPress', this._onBackAndroid)
}
_onBackAndroid = () => {
console.log(this.props.navigation.state.routeName)
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
return false;
}
this.lastBackPressed = Date.now();
toastShort('Press Again Exit App');
return true;
};
componentWillUnmount(){
BackHandler.removeEventListener('hardwareBackPress', this._onBackAndroid)
}
By design, react-navigation takes full control of the back button unless you handle the state yourself (by using something like Redux).
You can take a look at this issue, which is referencing a similar situation.

Check state before render for react native

I need to get the user's name the first time the app is run. Then after I want to skip the first screen and go directly to the second screen.
I'm using AsyncStorage.getItem("first") in the first screen to check if this is the first boot. If not, navigate('SecondScreen').
The problem is that the first screen flash for half a second before going to the second screen. Is there a way to fix this?
Render a loader and check AsyncStorage until the data is loaded, then navitage or render your component conditionally like so:
constructor(){
super()
this.state = {
hasName: null, loaded: false,
}
}
componentDidMount(){
this.fetchName()
}
fetchName(){
AsyncStorage.getItem('first')
.then((e, s)=>{
this.setState({hasName: s ? true : false, loaded: true})
});
}
loading(){
return(<View><Text>Loading...</Text></View>)
}
renderLoaded(){
if (this.state.hasName){
return(...)
} else {
navigate('secondScreen')
return(...)
}
}
render(){
if (this.state.loaded){
return this.renderLoaded()
}
return this.loading()
}
Basicaly, you want to do something similar...
Render "loading" screen while loading data. When done, rerender with main application.
class Application extends React.Component {
state = {
ready: false,
user: null,
}
async componentWillMount() {
const user = await AsyncStorage.getItem("user");
this.setState({
user,
ready: true
})
}
render() {
if (this.state.ready === false) {
// render "booting" screen while reading data from storate or remote server
return <Boot />;
}
if (this.state.user === null) {
// render Login screen
return <Login />
}
// Render main navigation stack for app
return <NavigatorStack user={this.state.user} />
}
}