Show Different Components using React-Native-Router-Flux Depending on Logged In Status

1k views Asked by At

My app is using react-native-router-flux to decide which Component to show the user depending on whether the user is

  • Logging in, or waiting for app to connect to backend (show <Loading />)
  • Logged in (show <Home />)
  • Not Logged In (show <Welcome />)

What is a recommended way to achieve that?

This example works perfect, except that it does not use react-native-router-flux. Anyway to modify the code to work with react-native-router-flux?

In my attempt below, calling Actions.loading() before the render function is called gives an error because Actions are not yet defined. Can the if statements be called after the render function?

Furthermore, everytime the props are updated, it causes a re-render of App, giving errors about how the scene keys already exist.

import React, { Component } from 'react';
import Meteor, { createContainer } from 'react-native-meteor';
import { Actions } from 'react-native-router-flux';

import Home from './components/home';
import Welcome from './components/welcome';
import Loading from './components/loading';
import settings from './config/settings';


Meteor.connect(settings.METEOR_URL);


const App = (props) => {

    const { status, user, loggingIn } = props;

    if (status.connected == false || loggingIn) {
        // Render <Loading />
        Actions.loading();
    } else if (user !== null) {
        // Render <Home />
        Actions.home();
    } else {
        // Render <Welcome />
        Actions.welcome();
    }

    return (
        <Router>
            <Scene key="root">
                <Scene key="home" component={Home} title="Home" hideNavBar={true} />
                <Scene key="welcome" component={Welcome} title="Welcome" hideNavBar={true} />
                <Scene key="loading" component={Loading} title="Loading" hideNavBar={true} />
            </Scene>
        </Router>
    )
}


export default createContainer(() => {
    return {
        status: Meteor.status(),
        user: Meteor.user(),
        loggingIn: Meteor.loggingIn()
    };
}, App)

Problem with using a Dispatch Component

Based on abeikverdi's suggestion, I created the following Dispatch component containing the logic to display one of the components <Loading />, <Home /> or <Welcome />.

Problem: When this.props.status.connected is true (React Native app connects to Meteor backend), this.props.user is always null for a while before the React Native app receives this data from the Meteor backend. But because it is initially evaluated as null, Action.welcome() will have executed. This behavior is incorrect when this.props.user eventually becomes not null after a second or two and the user should have been redirected to Actions.home() instead.

Any ideas?

export class Dispatch extends Component {
    constructor(props) {
        super(props);
    }

    componentWillUpdate() {
        if(this.props.status.connected == false) {
            Actions.loading();
        } else {
            console.log('meteor.user(): ', Meteor.user())
            if (this.props.user !== null) {
                Actions.home();
            } else {
                Actions.welcome();
            }
        }
    }

    render() {
        return (
            <View></View>
        )
    }
}


export default createContainer(() => {
    return {
        status: Meteor.status(),
        user: Meteor.user(),
        loggingIn: Meteor.loggingIn()
    };
}, Dispatch);

Errors/Warnings from logs react-native log-android

The following is the Android log output when the React Native app is started for a while before disconnecting it from the Meteor (DDP) server by killing the Meteor server.

Using console.log("<App /> render"), it appears that whenever the render function is called again after being passed new props by Meteor's createContainer, the Key is already defined error is triggered.

Anyway to get rid of this error/warning?

12-23 02:27:01.875 31197 19338 I ReactNativeJS: Running application "RNapp" with appParams: {"initialProps":{},"rootTag"
:1}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF
12-23 02:27:01.891 31197 19338 I ReactNativeJS: render
12-23 02:27:01.995 31197 19338 I ReactNativeJS: Connected to DDP server.
12-23 02:27:01.999 31197 19338 I ReactNativeJS: Connected to DDP server.
12-23 02:27:02.012 31197 19338 I ReactNativeJS: render
12-23 02:27:02.013 31197 19338 I ReactNativeJS: Key home is already defined!
12-23 02:27:02.013 31197 19338 I ReactNativeJS: Key welcome is already defined!
12-23 02:27:02.013 31197 19338 I ReactNativeJS: Key loading is already defined!
12-23 02:27:02.013 31197 19338 I ReactNativeJS: Key root is already defined!
12-23 02:27:34.592 31197 19338 I ReactNativeJS: Disconnected from DDP server.
12-23 02:27:34.593 31197 19338 I ReactNativeJS: Disconnected from DDP server.
12-23 02:27:34.599 31197 19338 I ReactNativeJS: <App /> render
12-23 02:27:34.599 31197 19338 I ReactNativeJS: Key home is already defined!
12-23 02:27:34.599 31197 19338 I ReactNativeJS: Key welcome is already defined!
12-23 02:27:34.599 31197 19338 I ReactNativeJS: Key loading is already defined!
12-23 02:27:34.599 31197 19338 I ReactNativeJS: Key root is already defined!
12-23 02:27:34.609 31197 19338 I ReactNativeJS: <Loading /> render
12-23 02:27:35.603 31197 19338 I ReactNativeJS: Disconnected from DDP server.
12-23 02:27:35.613 31197 19338 I ReactNativeJS: <App /> render
12-23 02:27:35.613 31197 19338 I ReactNativeJS: Key home is already defined!
12-23 02:27:35.613 31197 19338 I ReactNativeJS: Key welcome is already defined!
12-23 02:27:35.613 31197 19338 I ReactNativeJS: Key loading is already defined!
12-23 02:27:35.613 31197 19338 I ReactNativeJS: Key root is already defined!
12-23 02:27:45.599 31197 19338 I ReactNativeJS: Disconnected from DDP server.
12-23 02:27:45.616 31197 19338 I ReactNativeJS: <App /> render
12-23 02:27:45.616 31197 19338 I ReactNativeJS: Key home is already defined!
12-23 02:27:45.616 31197 19338 I ReactNativeJS: Key welcome is already defined!
12-23 02:27:45.616 31197 19338 I ReactNativeJS: Key loading is already defined!
12-23 02:27:45.616 31197 19338 I ReactNativeJS: Key root is already defined!

App Component

export class App extends Component {

    constructor(props) {
        super(props);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.status.connected == false) {
            Actions.loading();
        } else {
            if (nextProps.user !== null) {
                Actions.home();
            } else {
                Actions.welcome();
            }
        }
    }


    render() {
       console.log('<App /> render')
        return (
            <Router>
                <Scene key="root">
                    <Scene key="home" component={Home} title="Home" hideNavBar={true} />
                    <Scene key="welcome" component={Welcome} title="Welcome" hideNavBar={true} />
                    <Scene key="loading" component={Loading} title="Loading" hideNavBar={true} />
                    <Scene key="profile" component={Profile} title="Home" hideNavBar={true} />
                    <Scene key="history" component={History} title="Home" hideNavBar={true} />
                    <Scene key="search" component={Search} title="Home" hideNavBar={true} />
                </Scene>
            </Router>
        )       
    }

}


export default createContainer(() => {
  return {
    status: Meteor.status(),
    user: Meteor.user(),
    loggingIn: Meteor.loggingIn(),
  };
}, App);
2

There are 2 answers

1
Ahmed Ali On

You can achieve that by using initial prop , you read from store the data that will indicate if user is logged in or not for example using access_token .

Then you use initial={access_token} // for example

But keep in mind that it'll show the last scene with initial prop equal to true

// in componentDidMount
const currentUserAccessToken = store.getState().currentUserAccessToken;
        <Scene
            component={SignIn}
            hideNavBar
            initial={!currentUserAccessToken}
            key="signIn"
            title={I18n.t('sign_in')}
        />
6
abeikverdi On

You can make a dispatch page and put your logic there. So just add Dispatch as a scene and then navigate from there.

return (
        <Router>
            <Scene key="root">
                <Scene key="dispatch" component={Dispatch} hideNavBar={true} initial={true} />
                <Scene key="home" component={Home} title="Home" hideNavBar={true} />
                <Scene key="welcome" component={Welcome} title="Welcome" hideNavBar={true} />
                <Scene key="loading" component={Loading} title="Loading" hideNavBar={true} />
            </Scene>
        </Router>
    )

Update:
Seems like meteor is passing those arguments into props. Once the new props added then componentWillUpdate() wont be triggered. Instead you should put you navigation logic through Actions in componentWillReceiveProps(nextProps) as follow:

componentWillReceiveProps(nextProps) {
    if(nextProps.status.connected == false) {
        Actions.loading();
    } else {
        console.log('meteor.user(): ', Meteor.user())
        if (this.props.user !== null) {
            Actions.home();
        } else {
            Actions.welcome();
        }
    }
}

Then your navigation logic should be triggered after your login function is done. This should do the trick.