带有登录屏幕的反应导航

Posted

技术标签:

【中文标题】带有登录屏幕的反应导航【英文标题】:React-Navigation with Login Screen 【发布时间】:2017-08-10 03:25:27 【问题描述】:

我正在尝试使用 react-navigation 创建一个没有标签栏和标题的初始登录屏幕,一旦用户成功通过身份验证,将导航到另一个名为 LISTRECORD 的屏幕,它有一个标签栏、标题和没有后退按钮选项.有谁有这方面的经验可以分享一下吗?

总之,我试图通过 react-navigation 实现的目标如下所述......

屏幕 1:登录屏幕(无标题和标签栏) 已认证... 屏幕 2:LISTRECORD(标题、标签栏和无后退按钮) 标签栏还包含其他标签,用于导航到屏幕 3、屏幕 4...

【问题讨论】:

【参考方案1】:

2017 年 10 月 我发现这非常令人困惑,所以这是我从上到下的解决方案:

我建议开始一个新项目,然后将所有内容粘贴进去,然后再学习。我对代码进行了大量评论,因此如果您被困在任何特定领域,也许上下文可以帮助您重回正轨。

这篇文章展示了如何:

    完全设置 React Native 以运行 react-navigation 与 Redux 正确集成 处理 android 后退按钮 嵌套堆栈导航器 从子导航器导航到父导航器 重置导航堆栈 在从子导航到父导航时重置导航堆栈(嵌套)

index.js

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

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

src/App.js(这是最重要的文件,因为它汇集了所有碎片)

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/components/Auth/SignUpForm.js

我将在这里向您展示一个示例。这不是我的,我只是在这个摇摇晃晃的 *** 编辑器中为你打出来的。喜欢的话请给我点赞:)

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/components/Auth/LoginForm.js

我也会向你展示一种愚蠢的风格,带有超级涂料返回按钮

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/components/Auth/Splash.js

这是一个您可以玩弄的启动画面。我像使用高阶组件一样使用它:

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/components/Auth/login_actions.js

我将向您展示 initializeApp(),以便您获得一些想法:

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
    

在其他用例中,您可能更喜欢高阶组件。它们的工作原理与 React for web 完全相同。 Stephen Grider 的 Udemy 教程是最好的。

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)

你可以这样使用它:

import requireAuth from '../HOC/require_auth'

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

//将状态映射到道具

导出默认连接(mapStateToProps, actions)(requireAuth(RestrictedArea))

这就是我希望有人告诉我并向我展示的一切。

TLDR App.jsnav_reducer.js 文件绝对是最重要的。其余的都是老熟悉的。我的例子应该可以加速你进入一个野蛮的生产力机器。

[编辑] 这是我的注销操作创建者。如果您希望清除导航堆栈以使用户无法按下 Android 硬件返回按钮并返回需要身份验证的屏幕,您会发现它非常有用:

//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,
    

[额外编辑] 如何从子 Stack Navigator 导航到父 Stack Navigator?

如果您想从您的子堆栈导航器之一导航并重置堆栈,请执行以下操作:

    在组件内添加代码,您可以在其中使用this.props.navigation 制作像&lt;Something /&gt;这样的组件 将导航传递给它,像这样:&lt;Something navigation=this.props.navigation /&gt; 进入该组件的代码 注意您如何在此子组件中使用 this.props.navigation 现在您已经完成了,只需致电 this.props.navigation.navigate('OtherStackScreen'),您应该会看到 React Native 神奇地顺利到达那里

但是,我想在导航到父堆栈时重置整个堆栈。

    调用动作创建者或类似的东西(从第 6 步开始):this.props.handleSubmit(data, this.props.navigation) 进入动作创建器并观察可能存在的这段代码:

actionCreators.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' )]
        ))
    

我在企业级 React Native 应用程序中使用此代码,它运行良好。

react-navigation 就像函数式编程。它被设计成可以很好地组合在一起的小型“纯导航”片段进行处理。如果您采用上述策略,您会发现自己创建了可重复使用的导航逻辑,您可以根据需要随意粘贴。

【讨论】:

可能在启动画面和登录/退出时遇到一些异常,但您会轻松解决这些问题。自从这篇文章以来,我已经改变了几次我的流程,一旦你理解了我在这里发布的内容,这些变化就是trivial。由于连接了 Apollo Client,我的流程发生了变化。 关于 SO 的答案包含完整的高质量应用程序框架是非常罕见的。 -为此感谢你十亿次。我认为 react-navigation 库以及整个 RN 社区都会从这种广泛共享中受益。如果还没有,您愿意将其放入 Medium 帖子或其他格式中吗?我很乐意提供帮助(甚至自己写出来,但我不想因为您在这里出色的工作而受到赞扬)。 谢谢,我同意。我之所以成功,是因为弄清楚它的难度是我之前遇到的任何事情的两倍,学习 React Web 和 Native。我不希望任何人再次经历这种情况。我计划将其放入 Medium 帖子中,因为它不会在这里获得适当的可见性。我将在今天开始把它放在一起,并在其中引用你作为推动力。 @agm1984,感谢您的出色回答,这非常有帮助。您是否能够在 tabnavigator 中测试嵌套 stacknavigator 的重置功能。如果我们在作为 tabNavigator 的子级的 stacknavigator 中(假设 tab1 从 tab2 出来),当我试图从 tab1 中的屏幕移动到 tab2 时,它似乎不起作用 很好的答案。任何机会我们都可以看到它包含在 github 示例中。对于初学者来说,这将是一个极好的资源。【参考方案2】:

尽管 Manjeet 建议的方法可行,但它不是一个好的导航结构。

你应该做的是退后一步,在另一个层次上处理一切。

***导航器应该是呈现登录屏幕的堆栈导航器。这个最顶层导航器中的另一个屏幕应该是您应用的主导航器。当您的登录状态得到满足时,您将主堆栈重置为仅 Main-Navigator。

这种结构的原因是:

A- 如果您需要在未来登录之前添加入职信息怎么办?

B- 如果您需要在 Main-Navigation 环境之外导航(例如:您的主导航是选项卡,而您想要一个非选项卡视图)怎么办?

如果您的最顶层导航器是显示登录屏幕和其他导航器的 Stack-Navigator,那么您的应用的导航结构可以适当地扩展。

我不相信上面建议的登录屏幕或堆栈导航器的条件渲染是一个好主意....相信我...我已经走上了这条路。

【讨论】:

感谢您的友好分享帕克! :) 实际上我一开始就尝试过这种结构,但是由于某种原因,当我重新加载应用程序登录屏幕时会出现几秒钟,然后才移动到主仪表板 是的...这将在从存储中检索登录信息时发生。这就是为什么你实现一个启动画面,一旦从存储中读取正确的信息就会被关闭(成功或出错,不要被不可关闭的启动画面捕获) 哦,我同意,但我使用了原生解决方案作为 splashscren 您能否提供解释“将主堆栈重置为仅主导航器”的代码?【参考方案3】:

这就是我实现此功能的方式。

文件 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);

文件 1)我的 Root.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); //登录前的导航器

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;

【讨论】:

非常感谢!我会看看它。欣赏 嗨 Manjeet,我需要使用 Redux 来使您的代码工作吗?谢谢 是的,它只有 redux,你可以在没有 redux 的情况下使用更少的代码做类似的事情 谢谢曼吉特! ?我正试图围绕redux。会试一试。谢谢 我看不出您需要 App.js。你不能把那个渲染放在根渲染中吗?【参考方案4】:

这是我基于@parker recommendation的解决方案:

    创建一个***导航器,它应该是一个堆栈导航器 呈现登录屏幕。 此顶层中的另一个屏幕 navigator 应该是您应用的 Main-Navigator。 当您登录时 状态满足,您将主堆栈重置为 主导航器。

这段代码完成了上述工作。

创建一个新的 react-native 项目,然后将下面的代码复制到 index.ios.js 和/或 index.android.js 中以查看它的工作情况。

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);

【讨论】:

如果您希望登录在他们单击按钮后仍然存在,您将如何处理?您会检查他们是否已在登录文件中登录吗?或者您是否会制作另一个名为 index 的屏幕,在其中检查他们是否已经登录,并据此将他们导航到正确的屏幕? 感谢 sn-p。另外,当用户已经登录时,您将如何处理这种情况。在这种情况下,无需转到登录屏幕,我们只需转到主屏幕即可。 你能解释一下你的resetAction吗? index:0 指的是TopLevelNav,而action 指的是它的Main 路线吗?抱歉,新人在这里做出反应 这非常适合我的用例。谢谢!后续问题,你有没有实现过注销功能?无法找到类似的方法来注销并将导航重置为应用默认设置并返回登录屏幕。 对于注销,我认为您可以设置一个页面导航回原始导航方法,并以相同的方式重置堆栈。【参考方案5】:

现在在 react-navigation 站点 about the authentication flow 上有很好的文档。

【讨论】:

【参考方案6】:

react-navigation 现在有一个SwitchNavigator,它有助于在导航器之间进行所需的行为和切换。目前没有太多关于它的文档,但是该库创建了一个非常好的示例小吃,它显示了一个简单的身份验证流程实现。您可以查看here。

SwitchNavigator 参考

SwitchNavigator(RouteConfigs, SwitchNavigatorConfig)

来自文档的示例

const AppStack = StackNavigator( Home: HomeScreen, Other: OtherScreen );
const AuthStack = StackNavigator( SignIn: SignInScreen );

export default SwitchNavigator(
  
    AuthLoading: AuthLoadingScreen,
    App: AppStack,
    Auth: AuthStack,
  ,
  
    initialRouteName: 'AuthLoading',
  
);

【讨论】:

【参考方案7】:

您使用 react-navigation 很好,它可以很好地支持您的应用所需的大多数功能。这是我的建议

1) 验证时

React-native 有这个很好的特性状态变量,当视图改变时会重新渲染。您可以使用状态变量来了解应用用户的“状态”(经过身份验证的/访问者)。

这是一个简单的实现,用户通过按下登录按钮登录

用户登录的入口页面

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) 使用标题登录

登录查看


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;

记得删除登录屏幕中的样式属性并添加你的,包括导入,我将它们留在那里,因为它可以帮助你了解如何安排你的反应项目

但是它仍然可以在没有样式的情况下工作,因此您可以将它们取下,单击登录按钮将带您进入主屏幕,因为状态已更改并且必须根据新状态重新渲染视图

登录屏幕没有您需要的标题

带有标签的主屏幕

3) 带标题的标签 实现此功能的一般方法是在StackNavigator 中添加TabNavigator

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;

免责声明:代码可能会返回错误,因为某些文件可能会丢失或可能存在一些拼写错误,如果您必须复制此代码,请仔细检查详细信息并更改必要的地方。任何问题都可以粘贴为cmets。希望这对某人有所帮助。

您还可以删除选项卡配置中的图标或安装 react-native-vector 图标,这使选项卡很棒!

【讨论】:

【参考方案8】:

使标签栏和标题分开组件,并且只将它们包含在其他组件中。关于禁用“BACK”,文档中有关于“阻止导航操作”的部分:https://reactnavigation.org/docs/routers/

您应该可以将其用于屏幕 2。

【讨论】:

我只是想知道如果我们刷新应用会发生什么 你的意思是,如果它返回登录屏幕?您可以检查用户是否已经在登录屏幕上登录(无论如何您都必须在某处进行),然后在真正显示登录屏幕之前将他转发到屏幕 2。只是一个快速的想法。 是的,实现了这一点,创建了 2 个导航组件,1 个用于登录前其他用于登录后 您可以使用相关代码创建答案吗? (和其他人)也会很有趣:) 谢谢大家。感谢您的建议【参考方案9】:

我需要这个,但其他解决方案都不适合我。所以这是我使用抽屉登录的解决方案(后者只有在适当的身份验证后才能访问,并且里面的每个屏幕都有自己的导航堆栈)。我的代码有一个DrawerNavigator,但同样可以用于TabNavigator (createBottomTabNavigator)。

wrapScreen = stackNavigator =>
  createStackNavigator(stackNavigator, 
    defaultNavigationOptions: ( navigation ) => (
      headerStyle:  backgroundColor: "white" ,
      headerLeft: MenuButton(navigation)
    )
  );
    
const DrawerStack = createDrawerNavigator(
  
    // Menu Screens
    firstSection: wrapScreen( FirstScreen: FirstScreen ),
    secondSection: wrapScreen(
      SecondHomeScreen: SecondHomeScreen,
      SecondOptionScreen: SecondOptionScreen
    ),
    settingSection: wrapScreen( SettingScreen: SettingScreen ),
    aboutSection: wrapScreen( AboutScreen: AboutScreen )
  ,
  
    initialRouteName: "firstSection",
    gesturesEnabled: false,
    drawerPosition: "left",
    contentComponent: DrawerContainer
  
);

const PrimaryNav = createSwitchNavigator(
  
    loginStack: LoginScreen,
    appStack: DrawerStack
  ,
   initialRouteName: "loginStack" 
);

export default createAppContainer(PrimaryNav);

【讨论】:

谢谢弗朗索瓦。自从我在 2017 年初开始撰写这篇文章以来,反应导航 api 一直在发生变化,因此根据作为您的依赖项安装的 rn 版本,一些代码建议可能已经变得无关紧要。我有一个存储库(下面的链接),它使用反应导航,使用 graphql 进行身份验证。它还具有许多其他模块,例如分页和图像上传。其他模块仍在进行中。快乐编码。链接:github.com/myhendry/mobile/tree/master/src【参考方案10】:

如果您不希望从 LIST 页面到 LOGIN 页面没有返回按钮,您可以这样做:

static navigationOptions = 
  title: 'YOUR TITLE',
  headerLeft : null,
;

【讨论】:

这在 Android 上是行不通的,因为用户仍然可以点击设备上的后退按钮,它会将他们带回登录页面。

以上是关于带有登录屏幕的反应导航的主要内容,如果未能解决你的问题,请参考以下文章

在反应导航 5.x 中禁用导航回登录/注册屏幕

反应导航没有显示正确的屏幕

在反应导航中禁用后退按钮

来自组件的反应导航与博览会

你做啥来保持用户登录反应本机应用程序

反应导航以从 redux 操作文件导航