typescript - createStackNavigator och createBottomTabNavigator i React Navigation v.5
Jag är ny på React Native, och jag använder nya react-navigation v.5 i min react-native-app. När jag använder createStackNavigator och createBottomTabNavigator tillsammans i NavigationContainer får jag fel - "undefined is not an object" och "Another navigator is already registered for this container. You likely have multiple navigators under a single 'NavigationContainer' or 'Screen'. Make sure each navigator is under a separate 'Screen' container." (i React Navigation v.5). Snälla berätta var jag har fel?
AppNavigation.tsx
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { MainScreen } from "../screens/MainScreen";
import { PostScreen } from "../screens/PostScreen";
import { AboutScreen } from "../screens/AboutScreen";
import { BookedScreen } from "../screens/BookedScreen";
import { CreateScreen } from "../screens/CreateScreen";
import { THEME } from "../theme";
import { Platform } from "react-native";
import { AppHeaderIcon } from "../components/AppHeaderIcon";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
type RootStackParamList = {
Main: undefined;
Post: { postId?: string; date?: string; booked?: boolean };
About: undefined;
Booked: undefined;
Create: undefined;
};
const headerButtons = (title: string, icon: string, callback: () => void) => {
return (
<HeaderButtons HeaderButtonComponent={AppHeaderIcon}>
<Item title={title} iconName={icon} onPress={() => callback()} />
</HeaderButtons>
);
};
export const AppNavigation = () => {
const Stack = createStackNavigator<RootStackParamList>();
const Tab = createBottomTabNavigator();
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Main"
screenOptions={{
headerTintColor:
Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
headerStyle: {
backgroundColor:
Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
}
}}
>
<Stack.Screen
name="Main"
component={MainScreen}
options={{
headerTitle: "Мой блог",
headerRight: () =>
headerButtons("Сделать фото", "ios-camera", () =>
console.log("Press camera")
),
headerLeft: () =>
headerButtons("drower", "ios-menu", () =>
console.log("Press drower button")
)
}}
/>
<Stack.Screen
name="Post"
component={PostScreen}
options={({ route }) => ({
headerTitle: `Пост от ${new Date(
route.params.date
).toLocaleDateString()}`,
headerRight: () =>
headerButtons(
"star",
route.params.booked ? "ios-star" : "ios-star-outline",
() => console.log("Press star button")
)
})}
/>
<Stack.Screen name="About" component={AboutScreen} />
<Stack.Screen name="Booked" component={BookedScreen} />
<Stack.Screen name="Create" component={CreateScreen} />
</Stack.Navigator>
<Tab.Navigator>
<Tab.Screen name="Post" component={PostScreen} />
<Tab.Screen name="Booked" component={BookedScreen} />
</Tab.Navigator>
</NavigationContainer>
);
};
App.tsx
import React, { useState } from "react";
import { AppLoading } from "expo";
import { bootstrap } from "./src/bootstrap";
import { AppNavigation } from "./src/navigation/AppNavigation";
export default function App() {
const [isReady, setIsReady] = useState(false);
if (!isReady) {
//AppLoading - пока не завершится выполнение код дальше не пойдет
return (
<AppLoading
startAsync={bootstrap}
onFinish={() => setIsReady(true)}
onError={err => console.log('AppLoading error - ', err)}
/>
);
}
return <AppNavigation />;
}
Svar #1
Detta är det beteende vi vill ha från autentiseringsflödet: när användare loggar in vill vi kasta bort tillståndet för autentiseringsflödet och avmontera alla skärmar relaterade till autentisering, och när vi trycker på hårdvaruåterknappen förväntar vi oss att inte kunna gå tillbaka till autentiseringsflödet. Läs denna - https://reactnavigation.org/docs/en/auth-flow.html#render-the-navigator-content
Svar #2
Detta stycke hjälpte mig att förstå: "Du kan tänka på detta som att det finns separata navigeringsstackar inom varje flik, och det är exakt så vi kommer att modellera det i React Navigation". Jag rättade några felaktigheter i min kod:
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { MainScreen } from "../screens/MainScreen";
import { PostScreen } from "../screens/PostScreen";
import { BookedScreen } from "../screens/BookedScreen";
import { THEME } from "../theme";
import { Platform } from "react-native";
import { AppHeaderIcon } from "../components/AppHeaderIcon";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Ionicons } from "@expo/vector-icons";
type RootStackParamList = {
Main: undefined;
Post: { postId?: string; date?: string; booked?: boolean };
About: undefined;
Booked: undefined;
Create: undefined;
};
const Stack = createStackNavigator<RootStackParamList>();
const headerButtons = (title: string, icon: string, callback: () => void) => {
return (
<HeaderButtons HeaderButtonComponent={AppHeaderIcon}>
<Item title={title} iconName={icon} onPress={() => callback()} />
</HeaderButtons>
);
};
function PostNavigator() {
return (
<Stack.Navigator
screenOptions={{
headerTintColor: Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
headerStyle: {
backgroundColor:
Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
}
}}
>
<Stack.Screen
name="Main"
component={MainScreen}
options={{
headerTitle: "Мой блог",
headerRight: () =>
headerButtons("Сделать фото", "ios-camera", () =>
console.log("Press camera")
),
headerLeft: () =>
headerButtons("drower", "ios-menu", () =>
console.log("Press drower button")
)
}}
/>
<Stack.Screen
name="Post"
component={PostScreen}
options={({ route }) => ({
headerTitle: `Пост от ${new Date(
route.params.date
).toLocaleDateString()}`,
headerRight: () =>
headerButtons(
"star",
route.params.booked ? "ios-star" : "ios-star-outline",
() => console.log("Press star button")
)
})}
/>
</Stack.Navigator>
);
}
function BookedNavigator() {
return (
<Stack.Navigator
screenOptions={{
headerTintColor: Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
headerStyle: {
backgroundColor:
Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
}
}}
>
<Stack.Screen
name="Booked"
component={BookedScreen}
options={{
headerTitle: "Избранное",
headerRight: () =>
headerButtons("Сделать фото", "ios-camera", () =>
console.log("Press camera")
)
}}
/>
<Stack.Screen
name="Post"
component={PostScreen}
options={({ route }) => ({
headerTitle: `Пост от ${new Date(
route.params.date
).toLocaleDateString()}`,
headerRight: () =>
headerButtons(
"star",
route.params.booked ? "ios-star" : "ios-star-outline",
() => console.log("Press star button")
)
})}
/>
</Stack.Navigator>
);
}
export const AppNavigation = () => {
const Tab = createBottomTabNavigator();
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === "Post") {
iconName = focused ? "ios-list-box" : "ios-list";
} else if (route.name === "Booked") {
iconName = focused ? "ios-star" : "ios-star-outline";
}
return <Ionicons name={iconName} size={size} color={color} />;
}
})}
tabBarOptions={{
activeTintColor: THEME.MAIN_COLOR,
inactiveTintColor: "gray"
}}
>
<Tab.Screen name="Post" component={PostNavigator}
options={{
tabBarLabel: 'Все посты'
}}/>
<Tab.Screen name="Booked" component={BookedNavigator}
options={{
tabBarLabel: 'Избранное'
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
};
