美文网首页reactReact-NativeReact Native
react-navigation使用技巧(进阶篇)

react-navigation使用技巧(进阶篇)

作者: 挂着铃铛的兔 | 来源:发表于2017-07-13 17:08 被阅读15647次

    之前写过react-navigation使用技巧,那篇文章中主要讲了react-navigation的属性,封装和一些小技巧。虽然上篇文章中也有一些小技巧,但因为补充的比较晚,导致有些人没有看全,再加上我又找到了一些新的小玩意,特此写了本篇文章,如果之后还有新发现,也会再这篇文章中更新出来。

    如果遇到什么问题可以在评论区回复,或者加QQ群397885169询问

    1、让TabBar拥有点击事件

    react-navigationbeta13版本之后,已经默认支持点击事件了,它被放在了TabNavigator中,属性名为tabBarOnPress,内部提供了两个属性,一个方法({ route, index } , jumpToIndex),使用方法如下:

    tabBarOnPress:(obj)=>{
                console.log(obj);
    
                obj.jumpToIndex(obj.scene.index)
            },
    
    

    最新几个版本的react-navigation中,将代码和文件路径修改了,所以之前的方式失效了,在这里提供一个新的方法来修改Tabbar的点击事件。
    #### 原文链接

    一共要修改添加大大小小12处地方哦!涉及4个js文件。 这个修改已经被作者提交到react-navigation中了,希望在以后的更新中可以看到这次更新。

    使用方法

    static navigationOptions = ({navigation,screenProps}) => ({
        headerTitle:'首页',
        tabBarOnPress: (scene,jumpToIndex) => {
             console.log(scene); 
             jumpToIndex(scene.index)
        },
    });
    

    ~~ 注意:如果调用了tabBarOnPress方法,会默认关闭点击跳转事件,需要手动开启。!~~

    以下代码已被废弃

    感谢群友再遇见的分享。

    官方的api里面是没有提供tabBar的点击事件的,但在开发中经常需要监听tabBar的点击事件,解决这个问题的一种方法就是去修改源码,另一种是监听onTransitionEnd。(第二种方法这里先不讨论)

    源码中一共需要修改8处地方,包含3个js文件。源码在下面。

    1、react-navigation目录下src/views/TabView/TabBarBottom.js

    TabBarBottom.png

    2、react-navigation目录下src/views/TabView/TabBarTop.js

    TabBarTop.png

    3、react-navigation目录下src/views/TabView/TabView.js

    TabView.png

    #### 注意:第111行中的getScreenConfig需要手动改成getScreenOptions,要不然修改完源码会报错。

    #### 源码在这里,要注意修改getScreenOptions

    修改源码之后,在页面中这么用:

    static navigationOptions = ({navigation,screenProps}) => ({
        onTabPress:(()=>{
            alert('Home');
        })
    });
    

    2、修改页面的跳转动画

    在上一篇文章中,说了怎么将安卓的跳转动画改成iOS那样的,但其实react-navigation一共提供了4种跳转动画:
    1、从右向左: forHorizontal
    2、从下向上: forVertical
    3、安卓那种的从下向上: forFadeFromBottomAndroid
    4、无动画: forInitial

    但因为库的限制,想实现某些页面使用某种动画还是很难的,只能通过Demo中提供的笨方法来实现。

    首先还是导入react-navigation中的方法

    
    import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
    

    如果要改变跳转动画只能在StackNavigator中实现transitionConfig方法

    const MainStack = StackNavigator({
        Main:{
            screen:Main,
        },
    },{
        // mode:'modal',
        headerMode: 'screen',
        transitionConfig:()=>({
         // 只要修改最后的forVertical就可以实现不同的动画了。
          screenInterpolator:CardStackStyleInterpolator.forVertical,
        })
    });
    

    3、在Reset方法中传参

    react-navigation中的reset方法,应该都不陌生吧,重置路由。但有的需求是在登陆之后,重置路由并传递参数到某个页面,之前一直以为不可以,直到群友的react-native reset方法中传参的写法的这篇文章出现,感谢群友nextChampion的贡献。

    const resetAction = NavigationActions.reset({
        index: 0,
        actions: [
        NavigationActions.navigate({routeName: 'Home', params: { token: '123456' }})
        ]
    })
    
    this.props.navigation.dispatch(resetAction);
    

    在页面中可以通过如下方法得到参数。

    this.props.navigation.state.params.token 
    

    4、goBack返回指定页面

    在上篇文章中也有这个技巧,这里是总结重发。

    react-navigation目录下src/routers/StackRouter.js

    if (action.type === NavigationActions.BACK) {
        let backRouteIndex = null;
        if (action.key) {
    
          const backRoute = state.routes.find(
            /* $FlowFixMe */
            /* 修改源码 */
            route => route.routeName === action.key
            /* (route: *) => route.key === action.key */
          );
          /* $FlowFixMe */
          console.log('backRoute =====',backRoute);
          backRouteIndex = state.routes.indexOf(backRoute);
          console.log('backRoute =====',backRouteIndex);
        }
        if (backRouteIndex == null) {
          return StateUtils.pop(state);
        }
        if (backRouteIndex >= 0) {
          return {
            ...state,
            routes: state.routes.slice(0, backRouteIndex+1),
            index: backRouteIndex - 1 + 1,
          };
        }
      }
    

    注意:这样的修改源码之后,如果项目中使用Redux,并且启用了滑动返回,很会很大几率导致app卡死,所以并不太推荐这种方式,最好使用下面的方式

    5、react-navigation集成Redux

    可能会有人认为这样集成会麻烦,而且react-navigation内部实现也是类似Redux的高阶函数。我之前也是这么认为的,直到在识兔中,使用 Redux 修改首页图片之后 滑动返回 会导致app卡死。

    react-navigation集成Redux之后,能获取当前screenkeyrouteNmae等参数,goBack()的时候就可以直接取到key,而不用修改源码啦!

    具体操作步骤可以在识兔,一款用来识别图片的开源项目中查看。

    如果看不懂下面的修改步骤,可以先看下之前的文章Redux "使用"教程

    集成

    1、添加addNavigationHelpers

    import {
        StackNavigator,
        TabNavigator,
        addNavigationHelpers
    } from 'react-navigation';
    

    2、首先修改识兔中的App.js的导出方式

    const AppWithNavigationState = ({ dispatch, nav }) => (
        <MyApp navigation={addNavigationHelpers({ dispatch, state: nav })}/>
    );
    
    const mapStateToProps = state => ({
        nav: state.nav,
    });
    
    export default connect(mapStateToProps)(AppWithNavigationState);
    

    3、创建StackReducer

    // MyApp 是在App.js中导出的
    import { MyApp } from '../APP';
    
    
    export default function StackReducer(state , action) {
        let nextState;
        switch (action.type) {
            default:
                nextState = MyApp.router.getStateForAction(action, state);
                break;
        }
        return nextState || state;
    }
    

    4、修改rootReducer

    import nav from './StackReducer';
    
    const RootReducer = combineReducers({
        ...,
        nav,
    });
    
    export default RootReducer;
    

    使用

    1、修改ShiTu.js

    export default connect((state) => {
        ...
        const routes  = state.nav.routes;
        return {
            ...
            routes
        };
    },{...})(ShiTu)
    

    2、使用

    const {routes} = this.props;
    // 会有意想不到的惊喜哦!
    console.log(routes);
    

    感谢群友Roc的提供的react-navigation集成redux的 脚手架 可以通过它快速实现集成Redux

    6、安卓返回键在react-navigation中的正常监听

    之前使用Navigator的时候,可以通过下面的方法实现监听安卓的返回键,但使用了react-navigation后,会很迷茫,不知该怎么监听了。

    解决办法:集成Redux咯!??? 集成完Redux,在跳转之后,就能获得路由的length,可以通过length来判断当前页面是第几层。

    Navigator的方法

    componentWillMount() {
        if (Platform.OS === 'android') {
          BackAndroid.addEventListener('hardwareBackPress', this.onBackAndroid);
        }
      }
    componentWillUnmount() {
        if (Platform.OS === 'android') {
          BackAndroid.removeEventListener('hardwareBackPress', this.onBackAndroid);
        }
      }
    onBackAndroid = () => {
        const nav = this.navigator;
        const routers = nav.getCurrentRoutes();
        if (routers.length > 1) {
          nav.pop();
          return true;
        }
        return false;
      };
      ……
    }
    

    react-navigation的方式

    componentWillMount() {
        if (Platform.OS === 'android') {
            BackHandler.addEventListener('handwareBackPress',this.onBackAndroid)
        }
      }
    componentWillUnmount() {
        if (Platform.OS === 'android') {
            BackHandler.addEventListener('handwareBackPress',this.onBackAndroid)
        }
      }
    onBackAndroid = () => {
        const routers = nav.getCurrentRoutes();
        if (routers.length > 1) {
          return true;
        }
        return false;
      };
      ……
    }
    
    // 在跳转之后的页面中
    onBackAndroid = ()=> {
    
        const {routes} = this.props;
        console.log(routes);
        // alert(routes)
        if (routes.length > 1) {
            // 因为其他页面获得不到this.props,所以只能每个页面都写这个方法。
            this.props.navigation.goBack();
            return true;
        }
    }
    

    7、快速点击重复跳转的解决办法

    感谢群友编程大叔的贡献

    react-navigation目录下src/addNavigationHelpers.js

    可以点击这里查看原文链接

     export default function<S: *>(navigation: NavigationProp<S, NavigationAction>) {
      // 添加点击判断
      let debounce = true;
      return {
          ...navigation,
          goBack: (key?: ?string): boolean =>
              navigation.dispatch(
                  NavigationActions.back({
                      key: key === undefined ? navigation.state.key : key,
                  }),
              ),
          navigate: (routeName: string,
                     params?: NavigationParams,
                     action?: NavigationAction,): boolean => {
              if (debounce) {
                  debounce = false;
                  navigation.dispatch(
                      NavigationActions.navigate({
                          routeName,
                          params,
                          action,
                      }),
                  );
                  setTimeout(
                      () => {
                          debounce = true;
                      },
                  500,
                  );
                  return true;
              }
              return false;
          },
        /**
         * For updating current route params. For example the nav bar title and
         * buttons are based on the route params.
         * This means `setParams` can be used to update nav bar for example.
         */
        setParams: (params: NavigationParams): boolean =>
          navigation.dispatch(
            NavigationActions.setParams({
              params,
              key: navigation.state.key,
            }),
          ),
      };
    }
    

    8、安卓上,使用TextInput的时候会让TabBar顶起来的解决办法

    最简单的解决办法就是在android目录中,添加一句话

    目录:android/app/src/main/AndroidManifest.xml中,添加

     android:windowSoftInputMode="stateAlwaysHidden|adjustPan|adjustResize"
    

    ps:在iOS下如果想一劳永逸的解决键盘问题,请使用IQKeyBoardManager

    总结

    上述7个问题,是我暂时发现的可能在使用react-navigation遇到的问题,如果遇到其他问题欢迎加入QQ群397885169一起讨论,解决。

    如果在文章中遇到什么错误,欢迎加群反馈,吐槽。

    相关文章

      网友评论

      • 洁简:tabBarOnPress为何不起作用
      • whyme_:tabBarOnPress: (obj) => {
        obj.defaultHandler()//手动打开tab跳转
        }(查了下源码,"react-navigation": "^2.9.2",可用)
        洁简:那如何在screen中获取点击事件呢
      • 2da7ae756346:大神 goback 重复点击怎么解决??
      • LinMeiQi:使用redux中的 nav,获取routes,toutes数组总是只有一个,index都是0
      • 5ec1da87f063:你说的:如果调用了tabBarOnPress方法,会默认关闭点击跳转事件,需要手动开启.怎么开启啊.我这边点击tabbar.tabBarOnPress这个方法走了,但是界面不动啊.变成不会切换tab了.....
      • 667c19747d76:“如果调用了tabBarOnPress方法,会默认关闭点击跳转事件,需要手动开启”,请问怎么手动开启?
        whyme_:tabBarOnPress: (obj) => {
        obj.defaultHandler()//手动打开tab跳转
        }(查了下源码,"react-navigation": "^2.9.2",可用)
        whyme_:我也被这个问题困扰好久了,请问你这个问题解决了吗
      • 重案组之虎曹达华_:楼主 我想知道 如何判断当前页面是否是root 在不是root的页面统一重写左上角返回按钮 ,这样写一次就够了,但是没有找到好的办法
        whyme_:我也被这个问题困扰好久了,请问你这个问题解决了吗
      • 852351ac9863:感谢群友Roc的提供的react-navigation集成redux的 脚手架 可以通过它快速实现集成Redux。
        博主你好,能问下Roc的技术博客是什么呢?
      • sunney0:你好,运行有警告,Warning: isMounted(...) is deprecated in plain JavaScript React classes. isMounted函数被弃用,您没有遇到么,怎么解决这个警告?
      • lzh_coder:有一个问题想请教下,比如原生app的开发里面,我们都少不了要实现一个路由模块,可以实现在组件里面跳转页面的功能。那么在rn开发中,集成了react-navigation的情况下,是否也可以实现类似的功能,而不需要层层传递navigation属性,就可以在组件里面实现页面的路由跳转?使用react-navigation集成redux,可以实现这个效果吗?
      • a433e6dd680f:"react-navigation": "^1.0.0-beta.15"
        import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
      • 随遇而安_2750:楼主,可以分享一下DrawerNavigator的使用吗
      • 燶澧:TabNavigator 可以嵌套使用么 ,我嵌套的话里面的那个不显示内容 ,实现效果就像大多数新闻客户端那样:下面一个导航,上面一个。:relieved:
        挂着铃铛的兔:真的不推荐你们这么写。。。还不如用react-native-scrollable-tab-view
        eabb7599d405:我也是同样问题。。。有解决嘛。。。
      • 陆仁丙:这是一篇好文章,谁看谁知道~
        挂着铃铛的兔: @陆仁丙 谢谢
      • bb27bf1cf514:navigator默认提供getCurrentRoutes方法,,如果是navigation该怎么获取??貌似没有提供类似的方法啊?
        挂着铃铛的兔:集成redux
      • Style_Tender:你好,我自定义了一个替换场景的方法
        const prevGetStateForActionHomeStack = SimpleApp.router.getStateForAction;

        SimpleApp.router.getStateForAction = (action, state) => {
        if (state && action.type === 'ReplaceCurrentScreen') {
        const routes = state.routes.slice(0, state.routes.length - 1);
        routes.push(action);

        return {
        ...state,
        routes,
        index: routes.length - 1,
        };
        }
        return prevGetStateForActionHomeStack(action, state);
        };
        这里面一共有三个页面A,B,C,其中C是export的一个TabNavigator。从A->ReplaceCurrentScreen->B时,正常,点击返回就直接退出了。再从B->ReplaceCurrentScreen->C时,就报错了:Expect nav state to have routes and index, {"key":"C","type":"ReplaceCurrentScreen","routeName":"C"}。。。问下这是为什么呢
        Style_Tender:服了,随便试了一下弄好了。。。
        Style_Tender:直接this.props.navigation.navigate('C');却可以跳转过去
      • 19ecfd566409:兔神,最近上项目用的之前的react-native-tab-navigator,感觉满足不了需要了。想换个导航,这个是目前性价比最高的么?
        逆袭之前:可以试试原生实现的raect-native-navigation
        19ecfd566409:@挂着铃铛的兔 ok
        挂着铃铛的兔:@langxulei 肯定的咯。。。官方推荐
      • uuuuuuw:android物理返回 现在必须要集成redux才可以监听了是么 看你的文章
        挂着铃铛的兔:@独立团李李云龙 不一定
      • Rocky_92d5:tab点击弄好了,我之前是在
        const Tab = TabNavigator(
        中定义的,这是不行的。
        要在scene中的
        static navigationOptions = ({ navigation }) => ({
        这里面写onTabPress
        Twenty_:兄弟, 我貌似遇到你这种问题了,

        static navigationOptions = ({ navigation }) => ({
        title: '订单列表',
        onTabPress:(()=>{
        console.log('11111')
        }),
        })
        我使用onTabPress 貌似输出不了
      • 3399683fa84d:按照你的方式修改了,还是报错,getTabPressCallback is not a function
        3399683fa84d:@挂着铃铛的兔 改了
        挂着铃铛的兔:@JamesBond_12b5 注意事项改了吗
      • 无星灬:有没有OC向RN发送事件的demo,包括rn的部分。。。这地方我怎么都监听不到
      • hejunbinlan:我想知道一下怎么修改源码?
        就是先npm install后直接在node_modules下找到react-navigation修改吗?
        挂着铃铛的兔:@hejunbinlan 是的。。
      • 绯色流火:总感觉这个东西不是特别好用。。
        而且我个人感觉,这东西只要管理好路由就行了,弄那么多组件出来。。好像有点不伦不类的意思。
        挂着铃铛的兔:@绯色流火 API确实不够完善,但主要流畅度够好
      • 5ec1da87f063:666666.干脆重新写个导航吧.
        挂着铃铛的兔:@5ec1da87f063 最近准备弄下别的导航了。。。
      • Adar哒:沙发

      本文标题:react-navigation使用技巧(进阶篇)

      本文链接:https://www.haomeiwen.com/subject/jixghxtx.html