react navigation 6.0 升级

1,902 阅读2分钟

最近需要维护老的 RN 项目,需要升级到最新的版本。升级下来,主要涉及第三方库适配 androidx 及 react-navigaiton6 的升级。androidx 的升级主要是一些库的升级,包括不维护的库 fork 或者发起 pr,相对比较常规。主要的工作量在 react-navigation 这一次层。(参照 5.x 的升级文档)

一般改动点

navigation 4.x 之前通过 route configuration 配置,5.x 以上通过组件的嵌套,和 react-router 比较相似。可参照文档,修改tab,nav嵌套关系

// 4.x
const RootStack = createStackNavigator({
    Home: { screen: HScreen, navigationOptions: { title: 'My app' },
})
// 5.x
const RootStack = () => {
    return (
        <Stack.Navigator>
            <Stack.Screen name='Home' component={HScreen} options={{ title: 'My app' }} />
        </Stack.Navigator>
    );
}

主要改动点

  • 传值(params)

    5.x 获取params,之前通过navigation拿到state,现在需要从route取,this.props.route 或者通过 useRoute 拿到route对象。

  • 事件订阅(events)

    订阅的事件只有(focus,blur),之前的willxx,didxx需要修改,remove的方式为了兼容hooks,改成函数返回的方式,既this.unsubsubscribe()

  • 跳转(dispatch action)

    action 改动比较大,之前的NavigationActions被移除,改成了CommonAction,之前比较臃肿的写法也变得简洁了些。

    // 4.x
    const navigateAction = NavigationActions.navigate({
    	routeName: 'Profile',
    	params: {},
    	action: NavigationActions.navigate({ routeName: 'SubProfileRoute' }),
    });
    
    // 5.x reset
    const navigateAction = actionToCustom(1, [{ name: 'Profile' }, { name: 'SubProfileRoute' }]);
    // 多级跳转使用 state 配置
    const navigateAction = CommonActions.reset({
    	index: 0,
    	params: { version: '6.x' },
    	routes: [{ name: 'Profile', state: { routes: [{ name: 'SubProfileRoute' }], index: 0 } }],
    });
    // 5.x goback
    const backAction = {
    	...CommonActions.goBack(),
    	// key从route中获取
    	source: key,
    };
    
    // dispatch
    navigation.dispatch(action);
    
  • withNavigation(子组件传递navigation)

    4.x版本子组件可以使用withNavigation包装,使用onRef获取组件对象引用。新版本移除withNavigation,需要手动处理onRef,可改成ref或者在子组件中处理下onRef

    // 通过 function component 包装传入 navigaiton,route
    const UserBannerWrap = (props)=>{
     const navigation = useNavigation();
     const route = useRoute();
     const bannerRef = useRef();
     useEffect(()=>{
         props.onRef && props.onRef(bannerRef.current)
     })
     return (
         // 可兼容 class component
         <View {...props} navigation={navigation} route={route} ref={bannerRef} />
     );
    }
    // withNavigation 可从 @react-navigation/compat 包中获取,兼容老的Api,但是获取route中的params,还需要手动处理,感觉没有太必要
    const UserBannerWrap = withNavigation(ClassView)
    
  • 自定义路由(拦截action)

    5.x版本无法通过顶层的navigator component获取getStateForAction及action changge的方法。如需要拦截action type,比如BACK,RESET等,需要修改createStackNavigator,createBottomTabNavigator的实现方法。

    比如要拦截navStack,可查看createStackNavigator的实现代码,一般在同名的xx.d.ts的根目录的src下,也可直接查看package.json的types机source的路径。然后复制下,可参照文档

    一般只需要从实例化Router(可为StackRouter,tabRouter只是类型不同), 然后即可拦截getStateForAction,getStateForRouteNamesChange等方法,结合业务处理跳转。在通过useNavigationBuilder方法中传入自定义的Router,导出即可。