react-navigation及dva使用(三)

时间:2022-11-30 20:21:49

关于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回退到任何界面,官方文档提供的方法是这样的:

react-navigation及dva使用(三)

也就是根据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目录下

react-navigation及dva使用(三)

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