react-navigation及dva使用(三)

来源:互联网 发布:好用的男士爽肤水 知乎 编辑:程序博客网 时间:2024/06/08 17:00

关于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,不能说问及必答,只能说知无不言

原创粉丝点击