react-navigation使用技巧(进阶篇)
之前写过react-navigation使用技巧,那篇文章中主要讲了react-navigation的属性,封装和一些小技巧。虽然上篇文章中也有一些小技巧,但因为补充的比较晚,导致有些人没有看全,再加上我又找到了一些新的小玩意,特此写了本篇文章,如果之后还有新发现,也会再这篇文章中更新出来。
如果遇到什么问题可以在评论区回复,或者加QQ群397885169询问
1、让TabBar拥有点击事件
在react-navigation
beta13版本之后,已经默认支持点击事件了,它被放在了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
2、react-navigation目录下src/views/TabView/TabBarTop.js
3、react-navigation目录下src/views/TabView/TabView.js
#### 注意:第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之后,能获取当前screen
的key
,routeNmae
等参数,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
一起讨论,解决。
如果在文章中遇到什么错误,欢迎加群反馈,吐槽。