【React Native教程】Stack Navigation與Tab Navigation巢狀最佳實踐

語言: CN / TW / HK

上一篇文章詳細講解了 react-navigation 的使用,這篇文章主要說一下 Stack NavigationTab Navigation 巢狀的問題。

在iOS原生開發中,一般是在 TabController 裡巢狀 NavigationController ,也就是說底部導航控制器裡放多個堆疊導航控制器,每個堆疊導航控制器控制有獨立的堆疊和狀態。

但如果在使用 react-navigation 進行這種巢狀方式,由於根控制器是底部的 TabNavigation ,每次跳轉到子控制器時,底部的導航欄不會隱藏。官方文件說可以用屬性更改的方法隱藏底部導航欄,但不推薦,會影響效能。因此本文主要講解如何使用堆疊導航器中巢狀底部導航控制器來解決這個問題。

實現方式

首先將 Main 控制器放入 Stack Navigation 中:

class App extends Component {

	render() {
	    return (
	        <NavigationContainer>
	            <Stack.Navigator>
	                <Stack.Screen
	                    key="Main"
	                    name="Main"
	                    component={Main}
	                    options={({route}) => ({
	                        headerTitle: route.name
	                    })}
	                />
	            </Stack.Navigator>
	        </NavigationContainer>
	    );
	}
	
}

然後實現 Main 控制器

const Main = ({navigation, route}) => {

    return (
        <Tab.Navigator
            screenOptions={({ route }) => ({
                tabBarIcon: ({ focused, color, size }) => {``
                    return (
                        <Image style = {{ width: 25, height: 25 }}
                               source = {this.iconImage(route, focused)}
                        />);
                },
            })}
            tabBarOptions={{
                activeTintColor: '#32B7FF',
                inactiveTintColor: 'gray',
            }}
        >
            <Tab.Screen name="Home" component={HomeScreen} options={{title: 'Home'}}/>
            <Tab.Screen name="Settings" component={SettingsScreen} options={{title: 'Settings'}}/>
        </Tab.Navigator>
    );
}

function iconImage(route, focused) {
    let image;
    if (route.name === 'Home') {
        image = focused
            ? require('../../images/homePage_sel.png')
            : require('../../images/homePage_nor.png');
    } else {
        image = focused
            ? require('../../images/setting_sel.png')
            : require('../../images/setting_nor.png');
    }
    return image;
}

上面 HomeSetting 頁面的實現這裡就不再贅述,實現之後執行會發一 HomeSetting 頂部導航欄的標題和樣式都是相同的,也就是 Main 控制器的。這是由於 Tab Navigation 作為 Stack Navigation 的根控制器,頂部導航欄的標題都是按照根控制器的設定來顯示的。要實現 Tab Navigaiton 的子控制器都有自己的獨立頂部導航欄,需要使用 ReactHook 方法,在 return 前面加上:

React.useLayoutEffect(() => {
    navigation.setOptions({
      headerTitle: this.getHeaderTitle(route),
      headerRight: this.getHeaderRight(route),
      headerLeft: this.getHeaderLeft(route),
    });
  }, [navigation, route]);
  
getHeaderTitle(route) {
  const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
  switch (routeName) {
    case 'Home':
      return '首頁';
	case 'Settings':
      return '系統設定';
  }
}

這裡使用了 ReactuseLayoutEffect 方法,這個方法會從 DOM 裡讀取佈局並同步把新佈局添加了佈局更新計劃中,等一下次重新繪製就會更新上去。在方法中通過 route 名稱動態地改變了導航欄的標題和左右側的按鈕,當底部導航欄按鈕被點選進行切換時,頂底的導航欄顯示也會同步更新。

至此, Stack NavigationTab Navigation 巢狀時保證跳轉時隱藏底部導航欄的最佳實踐就完成了。