react-navigation及dva使用(三)

关于react-navigation,由于是社区维护的路由库,所以更新和问题会比较多,版本更新也比较快,我用的2个版本比较多,一个是beta7版本,现在master是beta11(截止到7月4日为止),关于react-navigation和redux进行绑定,看个人项目及偏好,这是牛叔的react-native dvastarter 点击打开链接 

我先说下两者的区别,

1,beta7版本,不允许嵌套跳转,请看场景:假设现在的路由栈是这样的:

const MainNavigator = StackNavigator(
    {
        HomeNavigator: {screen: HomeNavigator},
        MyMemberShip: {screen: MyMemberShip},//我的推荐人
      
    },
    {
        headerMode: 'none',
        mode: 'modal',
        navigationOptions: {
            cardStack: {
                gesturesEnabled: false,
            },
        },
    }
    ,
    )
;

HomeNavigator是一个TabNavigator,如果我想从MyMemberShip跳转到HomeNavigator中其中的一个tab页,直接navigate是做不到的,不可以嵌套跳转,如想做到需要这样做,我用的是redux的写法,其他写法自行修改:

This.props.dispatch(NavigationActions.navigate({routeName:’HomeNavigator’},action:[NavigationAction.navigate({routeName:’xxxTab’})])

其中xxxtab 是你要跳转tab页的名字

而beta11版本,还是刚才的场景,只需要navigate到某个tab页面即可,可以nested navigate;

2,监听跳转,beta7版本可以这样用,

    render() {
        const {dispatch, router} = this.props
        const navigation = addNavigationHelpers({dispatch, state: router})

        return (
            <View style={{flex: 1}}>
               
                <AppNavigator
                    navigation={navigation}
                    onNavigationStateChange={(prevState, currentState) => {
                        const currentScreen = getCurrentRouteName(currentState);
                        const prevScreen = getCurrentRouteName(prevState);

                        if (prevScreen == 'PayManager' && currentScreen == 'SubmitOrder') {
                            //做自己的操作,如刷新页面
                        }
                       
                    }}
                />


            </View>
        )
    }

即,navigation可以作为props进行传递,同时也在redux中进行存储,beta11不允许,只能在相应的models里面做监听和其他操作,如tab页切换的时候刷新相应的界面,或者动态显示tabbar,

                    if(payload.type=='Navigation/NAVIGATE'&&payload.routeName=='WorkSpace') {
                        payload.params = {tabBarVisible: false}
                        payload.type='Navigation/SET_PARAMS'
                        payload.key='WorkSpace'
                        yield put({
                            type: 'apply',
                            payload,
                        })

                    }

3,back回退到任何界面,官方文档提供的方法是这样的:


也就是根据key来back,这里需要明白2个地方,第一是如果back({null}),即回退到上一级,第二是 如果现在的路由栈是 A-B-C-D,如果想从D回到A,需要拿到B的key,然后进行如下操作:

this.props.navigation.dispatch(Navigation.back({key:'KeyofB'}))

,明白了这两点,才能更好的进行back操作,

因为screen在初始化的时候key是一个随机数,所以我们需要通过总的路由栈拿到需要进行back操作的key,进而进行back操作,

如果navigation和redux进行绑定,可以在任意screen 拿到路由栈,进行如下操作:

function getCurrentScreens(navigationState) {
    if (!navigationState) {
        return null
    }
    const route = navigationState.routes[navigationState.index]
    if (route.routes) {
        return getCurrentScreens(route)
    }
    return navigationState.routes
}
此方法是找到当前的路由栈,因为app有可能存在多个路由栈嵌套,需要拿到当前页面所在的路由栈,接下来做自己的业务操作:

  goBack(){
        const nowRouters= getCurrentScreens(this.props.router)
        console.log(nowRouters)

        //goback
        const backAction = NavigationActions.back({
            key: nowRouters[1].key  //key根据自己的业务改变
        })
        this.props.dispatch(backAction)
    }

这样就可以做到 回退两级,三级,甚至多级,当然,你还可以根据这个逻辑,自己做个simgletone 模式,比如说在商品详情和购物车之间互跳多次,而我们只需要在路由栈中保留一次,也可以根据上面的方法,判断此screenName是否存在,存在back到这个界面,而不是navigate,抛砖引玉,

4,跳转卡顿,第一是跳转动画和网络请求冲突,解决办法,在navigate过去的界面进行如下操作,

    componentDidMount() {
        //等待动画完成
        InteractionManager.runAfterInteractions(() => {
            this.props.dispatch(createAction('info/detail')(this.props.navigation.state.contentId))

        })

    }

第二是navigation进行操作的时候会输出很多log,把他屏蔽掉会路由操作会流畅很多,尤其是android端,在最新版本(0.46)中已经默认不输出任何跳转log,在程序的入口进行如下操作:

if(!__DEV__){console.log = ()=>{},console.warn = ()=>{}}  //是否为开发模式

5,再次点击tab重新加载页面,如新闻类的,再次点击tab重新加载数据

tabBarComponent:  ({jumpToIndex, ...props, navigation}) => 
(   <TabBarBottom {...props} jumpToIndex={index => {   
  const { state } = navigation,          
 { routes } = state;     
if (state.index === index && routes[index].index !== 0) {      
 navigation.dispatch(NavigationActions.reset({        
 index,        
 key: routes[index].key,        
 actions: [          
 NavigationActions.navigate({ routeName: routes[index].routeName })         
]       
})     
} 
else {       jumpToIndex(index);     }   
))/> )


6,tab页面弹出键盘,tabbar被顶到键盘上面,处理如下

componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow',this._keyboardDidShow.bind(this));
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide.bind(this));
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow() {
this.props.navigation.setParams({tabBar:{visible:false}})
}
_keyboardDidHide() {
debugger;
this.props.navigation.setParams({tabBar:{visible:true}})
}

7,增加tabbar模式‘left‘和’right‘,即侧边栏,这个对于手机端影响不大,主要是pad端,文档中并没有提供,只能修改源码实现,代码如下

首先在node-modules目录下


复制TabBarBottom到当前目录,并更改style,

const styles = StyleSheet.create({
  tabBar: {
    width:100,    //根据个人需求更改宽度
    height: '100%',
    borderRightWidth: StyleSheet.hairlineWidth,
    borderRightColor: 'rgba(0, 0, 0, .2)',
    backgroundColor: '#f4f4f4', // Default background color in iOS 10
  },

tab的样式可以在这进行修改

tab: {
    flex: 1,
      flexDirection:'row',
      alignItems: 'center',
    // justifyContent: 'flex-start',
  },


第二步,在node_modules/react-native-tab-view/src/TabViewAnimated.js中的render方法修改如下:

 render() {
    const {
      /* eslint-disable no-unused-vars */
      navigationState,
      onRequestChangeTab,
      onChangePosition,
      canJumpToTab,
      lazy,
      initialLayout,
      renderScene,
      /* eslint-enable no-unused-vars */
      renderPager,
      renderHeader,
      renderFooter,
        renderRight,  //更改
        renderLeft,   //更改
      ...rest
    } = this.props;
    const props = this._buildSceneRendererProps();
      const isRow=renderRight||renderLeft
    return (
      <View
        onLayout={this._handleLayout}
        loaded={this.state.loaded}
        style={[styles.container, this.props.style,
        {flexDirection:isRow?'row':'column'}]} //更改
      >
        {/*更改*/}
        {!isRow&&renderHeader && <View collapsable={false}>{renderHeader(props)}</View>}
        {isRow&&renderLeft && <View collapsable={false}>{renderLeft(props)}</View>}
        {renderPager({
          ...props,
          ...rest,
          children: navigationState.routes.map((route, index) =>
            this._renderScene({
              ...props,
              route,
              index,
              focused: index === navigationState.index,
            }),
          ),
        })}
        {!isRow&&renderFooter && <View collapsable={false}>{renderFooter(props)}</View>}
        {/*更改*/}
        {isRow&&renderRight && <View collapsable={false}>{renderRight(props)}</View>}
      </View>
    );
  }

第三步,在node_modules/react-navigation/src/views/TabView/TabView.js 中

18行

export type TabViewConfig = {
  tabBarComponent?: ReactClass<*>,
  tabBarPosition?: 'top' | 'bottom'|'left'|'right',  //更改
  tabBarOptions?: {},
  swipeEnabled?: boolean,
  animationEnabled?: boolean,
  lazy?: boolean,
};
34行

type Props = {
  tabBarComponent?: ReactClass<*>,
  tabBarPosition?: 'top' | 'bottom'|'left'|'right',
  tabBarOptions?: {},
  swipeEnabled?: boolean,
  animationEnabled?: boolean,
  lazy?: boolean,

145行
    let renderHeader;
    let renderFooter;
    let renderPager;
    let renderLeft; //
    let renderRight; //

    const { state } = this.props.navigation;
    const options = router.getScreenOptions(
      this.props.childNavigationProps[state.routes[state.index].key],
      screenProps || {}
    );

    const tabBarVisible = options.tabBarVisible == null
      ? true
      : options.tabBarVisible;

    if (tabBarComponent !== undefined && tabBarVisible) { //更改
      if (tabBarPosition === 'bottom') {
        renderFooter = this._renderTabBar;
      } else if (tabBarPosition === 'top'){ 
        renderHeader = this._renderTabBar;
      }else if (tabBarPosition === 'left'){  
          renderLeft = this._renderTabBar;
      }else{
          renderRight = this._renderTabBar; 
      }
    }

以及177行:

    const props = {
      lazy,
      animationEnabled,
      swipeEnabled,
      renderPager,
      renderHeader,
      renderFooter,
        renderLeft, // 
        renderRight, //
      renderScene: this._renderScene,
      onRequestChangeTab: this._handlePageChanged,
      navigationState: this.props.navigation.state,
      screenProps: this.props.screenProps,
      style: styles.container,
    };

这样 在使用TabNavigator 的时候 可以正常使用了:

const HomeNavigator = TabNavigator(
  {
    Home: { screen: Home },
    Account: { screen: Account },
  },
  {
    tabBarComponent: TabBarLeft,
    tabBarPosition: 'left',
    swipeEnabled: false,
    animationEnabled: false,
    lazyLoad: true,
  }
)

这个还是有宽高的问题,所以我自己fork了一下,需要的朋友可以私我

二,关于轮播图及图片压缩,

轮播图不管是swiper或者是viewpagerAndroid,在android端都会出现问题,需要我们做出相关操作,

如果是swiper加tabnavigator连用,会造成第一个tab页面图片不显示,beta11已经修复这个问题,说下在beta7版本的解决方案,代码如下

componentDidMount(){
        this.timer= setTimeout(()=>{
                this.setState({
                    visible:true
                })
            },300)
    }
 heaer=()=> {
        if(this.state.visible){
            // console.log(this.props.scrollImages)
        return (
                <Swiper height={200}
                        index={0}
                        autoplay={false}>
                    {/*轮播图*/}
                    {this.props.scrollImages.map((item, i) => <TouchableWithoutFeedback key={i} onPress={() => {
                        this.thumbPressHandle(i)
                    }}>
                        <FastImage style={styles.sImage}
                               source={{uri: IMG_URL + item.resUrl}}/>
                    </TouchableWithoutFeedback>)}
                </Swiper>

        )}else{
            return(<View style={{flex:1}}/>)
        }
    }
,另外,如果在安卓端遇到轮播图需要点击以后图片才会加载的情况,把swiper的

removeClippedSubviews  属性设置为false

按上述操作即可解决,图片压缩,建议使用这个库,

https://github.com/DylanVann/react-native-fast-image

使用简单,效果显著,推荐使用,

每天学习,码代码很累吧 ,加下群互相交流下咯 新建QQ群:657950514,不能说问及必答,只能说知无不言

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值