67 votes

React-Navigation avec écran de connexion

J'essaie d'utiliser react-navigation pour créer un écran initial de LOGIN qui n'a pas de barre d'onglets ni d'en-tête, et une fois que l'utilisateur a été authentifié avec succès, il navigue vers un autre écran appelé LISTRECORD qui a une barre d'onglets, un en-tête et aucune option de bouton retour. Quelqu'un a une expérience dans ce domaine et peut la partager ?

En résumé, ce que j'essaie de réaliser avec react-navigation est décrit ci-dessous...

Écran 1 : Écran de connexion (sans en-tête ni tablier)
Authentifié...
Écran 2 : LISTRECORD (En-tête, Tabbar et aucun bouton de retour)
La barre d'onglets contient également d'autres onglets pour la navigation vers l'écran 3, l'écran 4...

60voto

agm1984 Points 3539

Oct. 2017 J'ai trouvé cela ridiculement confus, alors voici ma solution en partant du haut vers le bas :

Je recommande de commencer un nouveau projet et de coller littéralement tout cela et de l'étudier ensuite. J'ai beaucoup commenté le code, donc si vous êtes bloqué sur un point spécifique, le contexte peut peut-être vous aider à vous remettre sur les rails.

Ce post montre comment :

  1. configurer complètement React Native pour exécuter react-navigation
  2. Intégrer correctement avec Redux
  3. Manipuler le bouton arrière d'Android
  4. Navigateurs de la pile Nest
  5. Naviguer d'un navigateur enfant à un navigateur parent
  6. Réinitialiser la pile de navigation
  7. Réinitialisation de la pile de navigation lors de la navigation d'un enfant à un parent (imbriqué)

index.js

import { AppRegistry } from 'react-native'
import App from './src/App'

AppRegistry.registerComponent('yourappname', () => App)

src/App.js (c'est le fichier le plus important car il rassemble tous les morceaux)

import React, { Component } from 'react'
// this will be used to make your Android hardware Back Button work
import { Platform, BackHandler } from 'react-native'
import { Provider, connect } from 'react-redux'
import { addNavigationHelpers } from 'react-navigation'
// this is your root-most navigation stack that can nest
// as many stacks as you want inside it
import { NavigationStack } from './navigation/nav_reducer'
// this is a plain ol' store
// same as const store = createStore(combinedReducers)
import store from './store'

// this creates a component, and uses magic to bring the navigation stack
// into all your components, and connects it to Redux
// don't mess with this or you won't get
// this.props.navigation.navigate('somewhere') everywhere you want it
// pro tip: that's what addNavigationHelpers() does
// the second half of the critical logic is coming up next in the nav_reducers.js file
class App extends Component {
    // when the app is mounted, fire up an event listener for Back Events
    // if the event listener returns false, Back will not occur (note that)
    // after some testing, this seems to be the best way to make
    // back always work and also never close the app
    componentWillMount() {
        if (Platform.OS !== 'android') return
        BackHandler.addEventListener('hardwareBackPress', () => {
            const { dispatch } = this.props
            dispatch({ type: 'Navigation/BACK' })
            return true
        })
    }

    // when the app is closed, remove the event listener
    componentWillUnmount() {
        if (Platform.OS === 'android') BackHandler.removeEventListener('hardwareBackPress')
    }

    render() {
        // slap the navigation helpers on (critical step)
        const { dispatch, nav } = this.props
        const navigation = addNavigationHelpers({
            dispatch,
            state: nav
        })
        return <NavigationStack navigation={navigation} />
    }
}

// nothing crazy here, just mapping Redux state to props for <App />
// then we create your root-level component ready to get all decorated up
const mapStateToProps = ({ nav }) => ({ nav })
const RootNavigationStack = connect(mapStateToProps)(App)

const Root = () => (
    <Provider store={store}>
        <RootNavigationStack />
    </Provider>
)

export default Root

src/navigation/nav_reducer.js

// NavigationActions is super critical
import { NavigationActions, StackNavigator } from 'react-navigation'
// these are literally whatever you want, standard components
// but, they are sitting in the root of the stack
import Splash from '../components/Auth/Splash'
import SignUp from '../components/Auth/SignupForm'
import SignIn from '../components/Auth/LoginForm'
import ForgottenPassword from '../components/Auth/ForgottenPassword'
// this is an example of a nested view, you might see after logging in
import Dashboard from '../components/Dashboard' // index.js file

const WeLoggedIn = StackNavigator({
    LandingPad: {             // if you don't specify an initial route,
        screen: Dashboard     // the first-declared one loads first
    }
}, {
    headerMode: 'none'
    initialRouteName: LandingPad // if you had 5 components in this stack,
})                               // this one would load when you do
                                 // this.props.navigation.navigate('WeLoggedIn')

// notice we are exporting this one. this turns into <RootNavigationStack />
// in your src/App.js file.
export const NavigationStack = StackNavigator({
    Splash: {
        screen: Splash
    },
    Signup: {
        screen: SignUp
    },
    Login: {
        screen: SignIn
    },
    ForgottenPassword: {
        screen: ForgottenPassword
    },
    WeLoggedIn: {
        screen: WeLoggedIn  // Notice how the screen is a StackNavigator
    }                       // now you understand how it works!
}, {
    headerMode: 'none'
})

// this is super critical for everything playing nice with Redux
// did you read the React-Navigation docs and recall when it said
// most people don't hook it up correctly? well, yours is now correct.
// this is translating your state properly into Redux on initialization    
const INITIAL_STATE = NavigationStack.router.getStateForAction(NavigationActions.init())

// this is pretty much a standard reducer, but it looks fancy
// all it cares about is "did the navigation stack change?"    
// if yes => update the stack
// if no => pass current stack through
export default (state = INITIAL_STATE, action) => {
    const nextState = NavigationStack.router.getStateForAction(action, state)

    return nextState || state
}

src/store/index.js

// remember when I said this is just a standard store
// this one is a little more advanced to show you
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { persistStore, autoRehydrate } from 'redux-persist'
import { AsyncStorage } from 'react-native'
// this pulls in your combinedReducers
// nav_reducer is one of them
import reducers from '../reducers'

const store = createStore(
    reducers,
    {},
    compose(
        applyMiddleware(thunk),
        autoRehydrate()
    )
)

persistStore(store, { storage: AsyncStorage, whitelist: [] })

// this exports it for App.js    
export default store

src/reducers.js

// here is my reducers file. I don't want any confusion
import { combineReducers } from 'redux'
// this is a standard reducer, same as you've been using since kindergarten
// with action types like LOGIN_SUCCESS, LOGIN_FAIL
import loginReducer from './components/Auth/login_reducer'
import navReducer from './navigation/nav_reducer'

export default combineReducers({
    auth: loginReducer,
    nav: navReducer
})

src/composants/Auth/SignUpForm.js

Je vais vous montrer un exemple ici. Ce n'est pas de moi, je l'ai juste tapé pour vous dans cet éditeur de StackOverflow bancal. Merci de me donner des pouces en l'air si vous l'appréciez :)

import React, { Component } from 'react'
import { View, Text, TouchableOpacity } from 'react-native

// notice how this.props.navigation just works, no mapStateToProps
// some wizards made this, not me
class SignUp extends Component {
    render() {
        return (
            <View>
                <Text>Signup</Text>
                <TouchableOpacity onPress={() => this.props.navigation.navigate('Login')}>
                    <Text>Go to Login View</Text>
                </TouchableOpacity>
            </View>
        )
    }
}

export default SignUp

src/composants/Auth/LoginForm.js

Je vous montrerai aussi un style débile, avec un bouton arrière super cool.

import React from 'react'
import { View, Text, TouchableOpacity } from 'react-native

// notice how we pass navigation in
const SignIn = ({ navigation }) => {
    return (
        <View>
            <Text>Log in</Text>
            <TouchableOpacity onPress={() => navigation.goBack(null)}>
                <Text>Go back to Sign up View</Text>
            </TouchableOpacity>
        </View>
    )
}

export default SignIn

src/composants/Auth/Splash.js

Voici un écran d'accueil avec lequel vous pouvez jouer. Je l'utilise comme un composant d'ordre supérieur :

import React, { Component } from 'react'
import { StyleSheet, View, Image, Text } from 'react-native'
// https://github.com/oblador/react-native-animatable
// this is a library you REALLY should be using
import * as Animatable from 'react-native-animatable' 
import { connect } from 'react-redux'
import { initializeApp } from './login_actions'

class Splash extends Component {
    constructor(props) {
        super(props)
        this.state = {}
    }

    componentWillMount() {
        setTimeout(() => this.props.initializeApp(), 2000)
    }

    componentWillReceiveProps(nextProps) {
        // if (!nextProps.authenticated) this.props.navigation.navigate('Login')
        if (nextProps.authenticated) this.props.navigation.navigate('WeLoggedIn')
    }

    render() {
        const { container, image, text } = styles
        return (
            <View style={container}>
                    <Image
                        style={image}
                        source={require('./logo.png')}
                    />

                    <Animatable.Text
                        style={text}
                        duration={1500}
                        animation="rubberBand"
                        easing="linear"
                        iterationCount="infinite"
                    >
                        Loading...
                    </Animatable.Text>
                    <Text>{(this.props.authenticated) ? 'LOGGED IN' : 'NOT LOGGED IN'}</Text>
            </View>
        )
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F0F0F0'
    },
    image: {
        height: 110,
        resizeMode: 'contain'
    },
    text: {
        marginTop: 50,
        fontSize: 15,
        color: '#1A1A1A'
    }
})

// my LOGIN_SUCCESS action creator flips state.auth.isAuthenticated to true    
// so this splash screen just watches it
const mapStateToProps = ({ auth }) => {
    return {
        authenticated: auth.isAuthenticated
    }
}

export default connect(mapStateToProps, { initializeApp })(Splash)

src/composants/Auth/login_actions.js

Je vais juste vous montrer initializeApp() pour que vous ayez quelques idées :

import {
    INITIALIZE_APP,
    CHECK_REMEMBER_ME,
    TOGGLE_REMEMBER_ME,
    LOGIN_INITIALIZE,
    LOGIN_SUCCESS,
    LOGIN_FAIL,
    LOGOUT
} from './login_types'

//INITIALIZE APP
// this isn't done, no try/catch and LOGIN_FAIL isn't hooked up
// but you get the idea
// if a valid JWT is detected, they will be navigated to WeLoggedIn
export const initializeApp = () => {
    return async (dispatch) => {
        dispatch({ type: INITIALIZE_APP })

        const user = await AsyncStorage.getItem('token')
            .catch((error) => dispatch({ type: LOGIN_FAIL, payload: error }))

        if (!user) return dispatch({ type: LOGIN_FAIL, payload: 'No Token' })

        return dispatch({
            type: LOGIN_SUCCESS,
            payload: user
        })
        // navigation.navigate('WeLoggedIn')
        // pass navigation into this function if you want
    }
}

Dans d'autres cas d'utilisation, vous pouvez préférer le composant d'ordre supérieur. Ils fonctionnent exactement de la même manière que React pour le web. Les tutoriels de Stephen Grider sur Udemy sont les meilleurs, point final.

src/HOC/require_auth.js

import React, { Component } from 'react'
import { connect } from 'react-redux'

export default function (ComposedComponent) {
    class Authentication extends Component {

        componentWillMount() {
            if (!this.props.authenticated) this.props.navigation.navigate('Login')
        }

        componentWillUpdate(nextProps) {
            if (!nextProps.authenticated) this.props.navigation.navigate('Login')
        }

        render() {
            return (
                <ComposedComponent {...this.props} />
            )
        }
    }

    const mapStateToProps = ({ auth }) => {
        return {
            authenticated: auth.isAuthenticated
        }
    }

    return connect(mapStateToProps)(Authentication)
}

Tu l'utilises comme ça :

import requireAuth from '../HOC/require_auth'

class RestrictedArea extends Component {
    // ... normal view component
}

//mappage de l'état aux accessoires

export default connect(mapStateToProps, actions)(requireAuth(RestrictedArea))

Voilà, c'est tout ce que j'aimerais que quelqu'un me dise et me montre.

TLDR Le site App.js y nav_reducer.js Les fichiers sont absolument les plus importants à obtenir. Le reste est bien connu. Mes exemples devraient faire de vous une machine de productivité sauvage.

[Voici le créateur de mon action de déconnexion. Vous le trouverez très utile si vous souhaitez effacer votre pile de navigation afin que l'utilisateur ne puisse pas appuyer sur le bouton Retour du matériel Android et revenir à un écran qui nécessite une authentification :

//LOGOUT
export const onLogout = (navigation) => {
    return async (dispatch) => {
        try {
            await AsyncStorage.removeItem('token')

            navigation.dispatch({
                type: 'Navigation/RESET',
                index: 0,
                actions: [{ type: 'Navigate', routeName: 'Login' }]
            })

            return dispatch({ type: LOGOUT })
        } catch (errors) {
            // pass the user through with no error
            // this restores INITIAL_STATE (see login_reducer.js)
            return dispatch({ type: LOGOUT })
        }
    }
}

// login_reducer.js
case LOGOUT: {
    return {
        ...INITIAL_STATE,
        isAuthenticated: false,
    }
}

[Comment naviguer d'un navigateur de pile enfant à un navigateur de pile parent ?

Si vous souhaitez naviguer à partir de l'un de vos navigateurs de pile enfant et réinitialiser la pile, procédez comme suit :

  1. Soyez à l'intérieur d'un composant ajoutant du code, où vous avez this.props.navigation disponible sur
  2. Créez un composant comme <Something />
  3. Passez la navigation dedans, comme ceci : <Something navigation={this.props.navigation} />
  4. Allez dans le code de ce composant
  5. Remarquez comment vous avez this.props.navigation disponible dans ce composant enfant
  6. Maintenant que vous avez terminé, il suffit d'appeler this.props.navigation.navigate('OtherStackScreen') et vous devriez voir React Native s'y rendre comme par magie sans problème.

Mais, je veux Réinitialiser l'ensemble de la pile tout en naviguant vers une pile parent.

  1. Appelez un créateur d'action ou quelque chose comme ça (en partant de l'étape 6) : this.props.handleSubmit(data, this.props.navigation)
  2. Allez dans le créateur d'action et observez ce code qui pourrait s'y trouver :

créateurs d'actions.js

// we need this to properly go from child to parent navigator while resetting
// if you do the normal reset method from a child navigator:
this.props.navigation.dispatch({
    type: 'Navigation/RESET',
    index: 0,
    actions: [{ type: 'Navigate', routeName: 'SomeRootScreen' }]
})

// you will see an error about big red error message and
// screen must be in your current stack 
// don't worry, I got your back. do this
// (remember, this is in the context of an action creator):
import { NavigationActions } from 'react-navigation'

// notice how we passed in this.props.navigation from the component,
// so we can just call it like Dan Abramov mixed with Gandolf
export const handleSubmit = (token, navigation) => async (dispatch) => {
    try {
        // lets do some operation with the token
        await AsyncStorage.setItem('token@E1', token)
        // let's dispatch some action that doesn't itself cause navigation
        // if you get into trouble, investigate shouldComponentUpdate()
        // and make it return false if it detects this action at this moment
        dispatch({ type: SOMETHING_COMPLETE })

        // heres where it gets 100% crazy and exhilarating
        return navigation.dispatch(NavigationActions.reset({
            // this says put it on index 0, aka top of stack
            index: 0,
            // this key: null is 9001% critical, this is what
            // actually wipes the stack
            key: null,
            // this navigates you to some screen that is in the Root Navigation Stack
            actions: [NavigationActions.navigate({ routeName: 'SomeRootScreen' })]
        }))
    } catch (error) {
        dispatch({ type: SOMETHING_COMPLETE })
        // User should login manually if token fails to save
        return navigation.dispatch(NavigationActions.reset({
            index: 0,
            key: null,
            actions: [NavigationActions.navigate({ routeName: 'Login' })]
        }))
    }
}

J'utilise ce code dans une application React Native d'entreprise, et il fonctionne parfaitement.

react-navigation est comme la programmation fonctionnelle. Elle est conçue pour être manipulée en petits fragments de "pure navigation" qui se composent bien ensemble. Si vous employez la stratégie décrite ci-dessus, vous vous retrouverez à créer une logique de navigation réutilisable que vous pourrez simplement coller selon vos besoins.

0 votes

Vous puede rencontrer quelques anomalies avec l'écran d'accueil et la connexion/déconnexion, mais vous les résoudrez facilement. J'ai modifié mon flux plusieurs fois depuis ce post, et les changements sont les suivants trivial une fois que vous aurez compris ce que j'ai posté ici. Mon flux a changé à cause du branchement du client Apollo.

1 votes

Il est assez rare qu'une réponse sur SO contienne un cadre d'application complet de haute qualité. - Je vous remercie un milliard de fois pour cela. Je pense que la bibliothèque react-navigation ainsi que l'ensemble de la communauté RN bénéficieraient d'un large partage de cette information. Si ce n'est pas déjà le cas, seriez-vous prêt à le mettre dans un billet Medium ou un autre format ? Je suis heureux d'aider (ou même de l'écrire moi-même, mais je ne veux pas prendre le crédit de votre travail fantastique ici).

1 votes

Merci, et je suis d'accord. Je l'ai fait parce que le comprendre était littéralement deux fois plus difficile que tout ce que j'avais rencontré avant, en apprenant React Web et Native. Je ne veux pas que quelqu'un vive cette expérience à nouveau. J'avais prévu de le mettre dans un post Medium puisqu'il n'aura pas une bonne visibilité ici. Je vais commencer à le faire aujourd'hui et je vous citerai comme source d'inspiration.

30voto

parker Points 1672

Bien que ce que Manjeet suggère fonctionne, ce n'est pas une bonne structure de navigation.

Ce que vous devriez faire, c'est prendre du recul et tout gérer à un autre niveau.

Le navigateur de premier niveau doit être un navigateur de pile qui rend un écran de connexion. Un autre écran dans ce navigateur de premier niveau devrait être le navigateur principal de votre application. Lorsque l'état de connexion est satisfait, vous réinitialisez la pile principale pour n'afficher que le navigateur principal.

La raison de cette structure est :

A- Que faire si vous avez besoin d'ajouter des informations sur l'embarquement avant la connexion dans le futur ?

B- Que se passe-t-il si vous avez besoin de naviguer en dehors de l'environnement de la navigation principale (ex : votre navigation principale est constituée d'onglets et vous voulez une vue sans onglet) ?

Si votre navigateur le plus haut est un Stack-Navigator qui présente des écrans de connexion et d'autres navigateurs, alors la structure de navigation de votre application peut évoluer correctement.

Je ne crois pas que le rendu conditionnel d'un écran de connexion ou d'un navigateur de pile, comme suggéré ci-dessus, soit une bonne idée.... croyez-moi... j'ai suivi cette voie.

0 votes

Merci pour ton partage Parker ! :)

0 votes

En fait, j'ai essayé cette structure au début, mais pour une raison quelconque, lorsque je recharge l'application, l'écran de connexion apparaît pendant quelques secondes avant de passer au tableau de bord principal.

1 votes

Oui... cela se produit lorsque les informations de connexion sont extraites du stockage. C'est pourquoi vous mettez en place un Splash Screen qui se ferme une fois que les informations appropriées sont lues à partir du stockage (soit avec succès, soit en cas d'erreur, ne vous laissez pas surprendre par un Splash Screen inadmissible).

12voto

Manjeet Singh Points 1614

Voici comment j'ai obtenu cette fonctionnalité.

Fichier 0)index.Android.js

'use strict'

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

import Root from 'src/containers/Root'

AppRegistry.registerComponent('Riduk', () => Root);

Fichier 1)ma Racine.js

class Root extends Component {
    constructor(props) {
      super(props);
      this.state = {
        authenticated:false,
        isLoading:true,
        store: configureStore(() => this.setState({isLoading: false})),
      };
  }

  componentDidMount() {
    //you can do check with authentication with fb, gmail and other right here
   /* firebase.auth().onAuthStateChanged((user) => {
      if (user) {
        api.resetRouteStack(dispatch, "UserProfile");
        console.log("authenticated", user);
      } else {
        api.resetRouteStack(dispatch, "Landing");
        console.log("authenticated", false);
      }
    });*/

  }

  render() {
    if (this.state.isLoading) {  //checking if the app fully loaded or not, splash screen can be rendered here
        return null;
      }
      return (

        <Provider store={this.state.store}>
          <App/>
        </Provider>

      );
  }
}
module.exports = Root;

2)App.js

import AppWithNavigationState,{AppBeforeLogin} from './AppNavigator';

class App extends Component{
    constructor(props){
        super(props);
    }

    render(){
        let {authenticated} = this.props;
        if(authenticated){
            return <AppWithNavigationState/>;
        }
        return <AppBeforeLogin/>

    }
}

export default connect(state =>({authenticated: state.user.authenticated}))(App);

3)AppNavigator.js

'use strict';

import React, {Component} from 'react';
import { View, BackAndroid, StatusBar,} from 'react-native';
import {
  NavigationActions,
  addNavigationHelpers,
  StackNavigator,
} from 'react-navigation';
import { connect} from 'react-redux';

import LandingScreen from 'src/screens/landingScreen';
import Login from 'src/screens/login'
import SignUp from 'src/screens/signUp'
import ForgotPassword from 'src/screens/forgotPassword'
import UserProfile from 'src/screens/userProfile'
import Drawer from 'src/screens/drawer'

const routesConfig = {
  //Splash:{screen:SplashScreen},
  Landing:{screen:LandingScreen},
  Login: { screen: Login },
  SignUp: { screen: SignUp },
  ForgotPassword: { screen: ForgotPassword },
  UserProfile:{screen:UserProfile},
};

export const AppNavigator = StackNavigator(routesConfig, {initialRouteName:'UserProfile'}); //navigator that will be used after login

export const AppBeforeLogin = StackNavigator(routesConfig) ; //navigateur pour avant la connexion

class AppWithNavigationState extends Component{
  constructor(props) {
    super(props);
    this.handleBackButton = this.handleBackButton.bind(this);
  }

  componentDidMount() {
    BackAndroid.addEventListener('hardwareBackPress', this.handleBackButton);
  }

  componentWillUnmount() {
    BackAndroid.removeEventListener('hardwareBackPress', this.handleBackButton);
  }

  //added to handle back button functionality on android
  handleBackButton() {
    const {nav, dispatch} = this.props;

    if (nav && nav.routes && nav.routes.length > 1) {
      dispatch(NavigationActions.back());
      return true;
    }
    return false;
  }

  render() {
    let {dispatch, nav} = this.props;

    return (
          <View style={styles.container}>
            {(api.isAndroid()) &&
              <StatusBar
                  backgroundColor="#C2185B"
                  barStyle="light-content"
              />
            }
            <AppNavigator navigation={addNavigationHelpers({ dispatch, state: nav })}/>
          </View>
    );
  }
};
export default connect(state =>({nav: state.nav}))(AppWithNavigationState);
//module.exports = AppWithNavigationState;

0 votes

Merci beaucoup ! Je vais le regarder. Je vous remercie

0 votes

Bonjour Manjeet, je dois utiliser Redux pour faire fonctionner votre code ? Merci

2 votes

Oui, c'est avec redux seulement, vous pouvez faire la même chose sans redux et en utilisant moins de code.

8voto

zechdc Points 1310

Voici ma solution basée sur Recommandation de @parker :

  1. Créez un navigateur de premier niveau et ce devrait être un navigateur de pile qui rend un écran de connexion.
  2. Un autre écran dans ce navigateur de haut niveau devrait être le navigateur principal de votre application.
  3. Lorsque votre état de connexion est satisfaite, vous réinitialisez la pile principale pour ne garder que le Navigateur principal.

Ce code fait le strict minimum pour accomplir ce qui précède.

Créez un nouveau projet react-native, puis copiez le code ci-dessous dans index.ios.js et/ou index.Android.js pour le voir fonctionner.

import React, { Component } from 'react';
import {
  AppRegistry,
  Text,
  Button
} from 'react-native';
import { StackNavigator, NavigationActions } from 'react-navigation';

const resetAction = NavigationActions.reset({
  index: 0,
  actions: [
    NavigationActions.navigate({ routeName: 'Main' })
  ]
});

class LoginScreen extends Component {
  login() {
    this.props.navigation.dispatch(resetAction);
  }

  render() {
    return <Button title='Login' onPress={() => {this.login()}} />;
  }
}

class FeedScreen extends Component {
  render() {
    return <Text>This is my main app screen after login</Text>;
  }
}

//Create the navigation
const MainNav = StackNavigator({
    Feed: { screen: FeedScreen },
});

const TopLevelNav = StackNavigator({
  Login: { screen: LoginScreen },
  Main: { screen: MainNav },
}, {
  headerMode: 'none',
});

AppRegistry.registerComponent('ReactNav2', () => TopLevelNav);

1 votes

Comment faire si vous voulez que la connexion persiste après qu'ils aient cliqué sur le bouton ? Vérifieriez-vous s'il s'est déjà connecté dans le fichier de connexion ? Ou bien créez-vous un autre écran appelé "index", dans lequel vous vérifiez s'il s'est déjà connecté et le dirigez vers le bon écran en fonction de cela ?

0 votes

Merci pour le snippet. Comment gérez-vous le cas où l'utilisateur s'est déjà connecté ? Dans ce scénario, il n'est pas nécessaire de passer par l'écran de connexion et nous pouvons simplement passer par la page principale.

0 votes

Pourriez-vous expliquer votre resetAction ? est index:0 se référant à TopLevelNav et l'action se référant alors à son Main route ? désolé, c'est nouveau de réagir ici

3voto

F.E Noel Nfebe Points 360

C'est bien que vous utilisiez react-navigation qui a un bon support pour la plupart des fonctionnalités dont votre application a besoin. Voici mon conseil

1) Sur l'authentification

React-native a cette fonctionnalité intéressante variables d'état qui, lorsqu'elles sont modifiées, sont rendues à nouveau. Vous pouvez utiliser des variables d'état pour comprendre l'"état" (authentifié/visiteur) des utilisateurs de votre application.

Voici une mise en œuvre simple où un utilisateur se connecte en appuyant sur un bouton de connexion.

Page d'entrée où l'utilisateur se connecte

import React from 'react';

import Home from './layouts/users/home/Home';
import Login from './layouts/public/login/Login';

class App extends React.Component {
  state = {
    isLoggedIn: false
  }

  componentDidMount() {
        //Do something here like hide splash screen
  }

  render(){
    if (this.state.isLoggedIn)
      return <Home />;
    else
      return <Login onLoginPress={() => this.setState({isLoggedIn: true})} />;
  }
}

export default App;

2) Connectez-vous avec l'en-tête

Login View

import React from 'react';
//Non react-native import
import { TabNavigator } from 'react-navigation'
import Icon from 'react-native-vector-icons/MaterialIcons'
import LoginStyles from './Style'
//Do all imports found in react-native here
import {
    View,
    Text,
    TextInput,
    StyleSheet,
    TouchableOpacity,
} from 'react-native';

class Login extends React.Component {
  render(){
    return (
      <View>
        <Text>
          Login area
        </Text>

        <TouchableOpacity style={LoginStyles.touchable} onPress={this.props.onLoginPress}>
          <Text style={LoginStyles.button}>
            Login
          </Text>
        </TouchableOpacity>
      </View>
    );
  }
}

export default Login;

N'oubliez pas de supprimer les attributs de style dans l'écran de connexion et d'ajouter les vôtres, y compris l'importation. Je les laisse là car cela peut vous aider à avoir une idée de la manière dont vous pouvez organiser votre projet de réaction.

Cependant, il fonctionne toujours sans les styles, vous pouvez donc les enlever. En cliquant sur le bouton de connexion, vous arriverez à l'écran d'accueil, car l'état a changé et la vue doit être redessinée en fonction du nouvel état.

L'écran de connexion est sans en-tête, comme vous l'avez demandé.

Écran d'accueil avec onglets

3) Onglets avec en-tête La méthode générale pour réaliser cette fonctionnalité est d'ajouter une fonction TabNavigator dans un StackNavigator .

import React from 'react';
import {
  DrawerNavigator,
  StackNavigator,
  TabNavigator,
  TabBarBottom,
  NavigationActions
} from 'react-navigation'

import Icon from 'react-native-vector-icons/MaterialIcons'

//Do all imports found in react-native here
import {
    View,
    Text,
    TextInput,
    StyleSheet,
    TouchableOpacity,
} from 'react-native';

class PicturesTab extends React.Component {
  static navigationOptions = {
    tabBarLabel: 'Pictures',
    // Note: By default the icon is only shown on iOS. Search the showIcon option below.
    tabBarIcon: ({ tintColor }) =>  (<Icon size={30} color={tintColor} name="photo" />),
  };

  render() { return <Text>Pictures</Text> }
}

class VideosTab extends React.Component {
  static navigationOptions = {
    tabBarLabel: 'Videos',
    tabBarIcon: ({ tintColor }) =>  (<Icon size={30} color={tintColor} name="videocam" />),
  };

  render() { return <Text>Videos</Text> }
}

const HomeTabs = TabNavigator({
  Pictures: {
    screen: PicturesTab,
  },
  Videos: {
    screen: VideosTab,
  },
}, {
    tabBarComponent: TabBarBottom,
    tabBarPosition: 'bottom',
    tabBarOptions: {
    //Thick teal #094545
    activeTintColor: '#094545',
    showLabel: false,
    activeBackgroundColor: '#094545',
    inactiveTintColor: '#bbb',
    activeTintColor: '#fff',    
  }
});

const HomeScreen = StackNavigator({
  HomeTabs : { screen: HomeTabs,
    navigationOptions: ({ navigation }) => ({
     // title :'title',
     // headerRight:'put some component here',
     // headerLeft:'put some component here',
     headerStyle: {
       backgroundColor: '#094545'
     }
   })
 },
});

export default HomeScreen;

Avis de non-responsabilité : Le code peut renvoyer des erreurs car certains fichiers peuvent être manquants ou des fautes de frappe peuvent être présentes. Vous devez vérifier attentivement les détails et les modifier si nécessaire si vous devez copier ce code. Tout problème peut être collé en tant que commentaire. J'espère que cela aidera quelqu'un.

Vous pouvez également supprimer les icônes dans la configuration des onglets ou installer les icônes react-native-vector qui rendent les onglets superbes !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X