关于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) {此方法是找到当前的路由栈,因为app有可能存在多个路由栈嵌套,需要拿到当前页面所在的路由栈,接下来做自己的业务操作:
if (!navigationState) {
return null
}
const route = navigationState.routes[navigationState.index]
if (route.routes) {
return getCurrentScreens(route)
}
return navigationState.routes
}
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 = {34行
tabBarComponent?: ReactClass<*>,
tabBarPosition?: 'top' | 'bottom'|'left'|'right', //更改
tabBarOptions?: {},
swipeEnabled?: boolean,
animationEnabled?: boolean,
lazy?: boolean,
};
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,不能说问及必答,只能说知无不言