如何优化React Navigation v3.0重复TabNavigator代码并导出默认导航选项?
我正在为毕业论文开发一款健身应用,里面包含大量训练项目页面。每个页面都需要使用React Navigation v3的BottomTabNavigator,而且已经实现了通过TabNavigator把父组件的props传递给子组件,但现在每个屏幕都要创建独立的TabNavigator,导致代码大量重复。想请教两个问题:
- 能不能把
defaultNavigationOptions导出到单独的组件里复用? - 有没有整体优化代码重复的方案?
附当前的重复代码片段:
import Chrono from "../../../assets/tools/Chrono" import Session from "./Session" import Resultats from "./Resultats" constructor(props) { super(props) this.state = { myPath: 'DeveloppeCouche', } this.AppContainer = createAppContainer(this.TabNavigator) } TabNavigator = createBottomTabNavigator({ MainPage, // 当前页面包含训练项目的文字和图片 Chrono, Session: { screen: () => <Session myPath={this.state.myPath} /> }, Resultats: { screen: () => <Resultats myPath={this.state.myPath} /> } }, { defaultNavigationOptions: ({ navigation }) => ({ tabBarIcon: ({ tintColor }) => { const { routeName } = navigation.state; let IconComponent = Ionicons; let iconName; if (routeName === 'MainPage') { iconName = 'md-home'; } else if (routeName === 'Chrono') { iconName = 'md-time'; } else if (routeName === 'Session') { iconName = 'md-save'; } else if (routeName === 'Resultats') { iconName = 'md-analytics'; } return <IconComponent name={iconName} size={25} color={tintColor} />; }, }), tabBarOptions: { activeTintColor: 'red', inactiveTintColor: 'gray', }, })
当然可以优化!针对你这种大量复用TabNavigator的场景,我们可以从抽离通用配置和封装可复用组件两个方向入手,彻底解决代码重复的问题。
1. 先把defaultNavigationOptions和通用配置抽离成独立模块
首先,把所有TabNavigator共用的配置(比如底部导航的图标规则、颜色设置)单独放到一个文件里,比如src/navigations/TabConfigs.js:
// TabConfigs.js import Ionicons from 'react-native-vector-icons/Ionicons'; // 抽离默认导航选项 export const defaultTabNavOptions = ({ navigation }) => ({ tabBarIcon: ({ tintColor }) => { const { routeName } = navigation.state; // 把图标映射做成对象,替代冗长的if-else,更易维护 const iconMap = { MainPage: 'md-home', Chrono: 'md-time', Session: 'md-save', Resultats: 'md-analytics' }; const iconName = iconMap[routeName]; return <Ionicons name={iconName} size={25} color={tintColor} />; }, }); // 抽离tabBarOptions通用配置 export const defaultTabBarOptions = { activeTintColor: 'red', inactiveTintColor: 'gray', };
这样所有需要TabNavigator的页面都可以直接导入这些配置,不用重复写图标判断逻辑了。
2. 封装通用的训练项目TabNavigator组件
接下来,我们可以把整个TabNavigator的创建逻辑封装成一个可复用的函数,接受自定义的训练主页面组件和需要传递的props作为参数,这样每个训练页面只需要传入自己的MainPage和myPath即可。
创建src/navigations/WorkoutTabNavigator.js:
// WorkoutTabNavigator.js import { createBottomTabNavigator, createAppContainer } from 'react-navigation'; import Chrono from "../../assets/tools/Chrono"; import Session from "../components/Session"; import Resultats from "../components/Resultats"; import { defaultTabNavOptions, defaultTabBarOptions } from './TabConfigs'; // 封装创建TabNavigator的函数 export const createWorkoutTabNavigator = (MainPage, customProps) => { const TabNavigator = createBottomTabNavigator( { MainPage, Chrono, Session: { screen: () => <Session {...customProps} /> }, Resultats: { screen: () => <Resultats {...customProps} /> } }, { defaultNavigationOptions: defaultTabNavOptions, tabBarOptions: defaultTabBarOptions, } ); return createAppContainer(TabNavigator); };
3. 在训练页面中使用封装好的组件
现在每个训练项目页面的代码会变得非常简洁,比如你的DeveloppeCouche页面:
import React from 'react'; import { createWorkoutTabNavigator } from '../navigations/WorkoutTabNavigator'; import DeveloppeCoucheMain from './DeveloppeCoucheMain'; // 该训练项目的专属MainPage class DeveloppeCouche extends React.Component { state = { myPath: 'DeveloppeCouche', }; render() { // 直接调用封装好的函数创建导航容器 const AppContainer = createWorkoutTabNavigator(DeveloppeCoucheMain, { myPath: this.state.myPath }); return <AppContainer />; } } export default DeveloppeCouche;
额外优化:处理state动态更新后的props传递
如果你的myPath可能会动态变化,上面的写法可能不会自动更新子组件的props。这时候可以把AppContainer的创建放到render方法里(如上面示例),或者改成组件形式实现更优雅的状态同步:
// 修改WorkoutTabNavigator.js为组件形式 export class WorkoutTabNavigator extends React.Component { render() { const { MainPage, screenProps } = this.props; const TabNavigator = createBottomTabNavigator( { MainPage, Chrono, Session: { screen: (navProps) => <Session {...navProps} {...screenProps} /> }, Resultats: { screen: (navProps) => <Resultats {...navProps} {...screenProps} /> } }, { defaultNavigationOptions: defaultTabNavOptions, tabBarOptions: defaultTabBarOptions, } ); const AppContainer = createAppContainer(TabNavigator); return <AppContainer />; } }
然后在训练页面使用:
render() { return ( <WorkoutTabNavigator MainPage={DeveloppeCoucheMain} screenProps={{ myPath: this.state.myPath }} /> ); }
这样不管myPath怎么变化,子组件都能实时拿到最新的props了。
内容的提问来源于stack exchange,提问作者Ulysses

