React Native 使用参数导航到另一个堆栈
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { enableScreens } from 'react-native-screens';
import { createNativeStackNavigator } from 'react-native-screens/native-stack';
enableScreens();
const Tab = createBottomTabNavigator();
const RootStack = createNativeStackNavigator();
const WardrobeStack = createNativeStackNavigator();
const OutfitStack = createNativeStackNavigator();
const CreateLookStack = createNativeStackNavigator();
const ProfileStack = createNativeStackNavigator();
export const WardrobeNavigator = () => {
return (
<WardrobeStack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName='Wardrobe'>
<WardrobeStack.Screen name="Wardrobe" component={Wardrobe} /> // <== navigate to here with params
<WardrobeStack.Screen name="Profile" component={Profile} />
<WardrobeStack.Screen name="AddFirstItem" component={AddFirstItem} />
<WardrobeStack.Screen name="RetakeContinue" component={RetakeContinue} />
<WardrobeStack.Screen name="CategoryBrand" component={CategoryBrand} />
<WardrobeStack.Screen name="DeleteCategory" component={DeleteCategory} />
</WardrobeStack.Navigator>
);
}
export const OutfitNavigator = () => {
return (
<OutfitStack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName='LookBook'>
<OutfitStack.Screen name="Outfits" component={Outfits} />
<OutfitStack.Screen name="LookBook" component={LookBook} />
<OutfitStack.Screen name="LookBookList" component={LookBookList} /> // <== navigate from here
<OutfitStack.Screen name="Profile" component={Profile} />
<OutfitStack.Screen name="ShoppingList" component={ShoppingList} />
</OutfitStack.Navigator>
);
}
export const CreateLookNavigator = () => {
return (
<CreateLookStack.Navigator
screenOptions={{
headerShown: false,
}}
initialRouteName="NewLook">
<CreateLookStack.Screen name="NewLook">
{props => (
<NewLook
{...props}
initialParams={{
lookSaveType: AppConstants.DEFAULT_LOOK_SAVE_TYPE,
}}
/>
)}
</CreateLookStack.Screen>
<CreateLookStack.Screen name="SaveLook" component={SaveLook} />
</CreateLookStack.Navigator>
);
}
export const ProfileNavigator = () => {
return (
<ProfileStack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName='ProfileOnbarding'>
<ProfileStack.Screen name="ProfileOnbarding" component={ProfileOnboarding} />
<ProfileStack.Screen name="ProfileAllSet" component={ProfileAllSet} />
</ProfileStack.Navigator>
);
}
export const RootNavigator = () => {
return (
<RootStack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName='Main'
mode='modal'>
<RootStack.Screen name="Main" component={BottomTabNavigator} />
<RootStack.Screen name="Brands" component={BrandStores} />
<RootStack.Screen name="ItemScreen" component={ItemScreen} />
<RootStack.Screen name="CreateLook" component={CreateLookNavigator} />
<RootStack.Screen name="ShoppingList" component={ShoppingList} />
<RootStack.Screen name="ProfileOnboarding" component={ProfileNavigator} />
{/* add full screen appear as modal */}
</RootStack.Navigator>
);
}
export const BottomTabNavigator = () => {
return (
<Tab.Navigator
initialRouteName="Outfits"
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
switch (route.name) {
case 'Explore':
return focused ? <ActiveTeleIcon /> : <InactiveTeleIcon />
case 'Bookings':
return focused ? <ActiveBookingIcon /> : <InactiveBookingIcon />
case 'Wardrobe':
return focused ? <ActiveWardrobeIcon /> : <InactiveWardrobeIcon />
case 'Outfits':
return focused ? <ActiveOutfitsIcon /> : <InactiveOutfitsIcon />
case 'Chat':
return focused ? <ActiveChatIcon /> : <InactiveChatIcon />
}
},
})}
tabBarOptions={{
activeTintColor: Colors.PRIMARY,
inactiveTintColor: Colors.SECONDARY_TEXT,
statusBarStyle: 'light-content',
style: styles.tabContainer,
labelStyle: styles.labelStyle,
allowFontScaling: true,
}}
>
<Tab.Screen
name="Explore"
component={Explore}
options={{
tabBarLabel: 'Explore'
}}
/>
<Tab.Screen
name="Bookings"
component={Bookings}
options={{
tabBarLabel: 'Bookings',
}}
/>
<Tab.Screen
name="Wardrobe"
component={WardrobeNavigator}
options={{
tabBarLabel: 'Wardrobe',
}}
/>
<Tab.Screen
name="Outfits"
component={OutfitNavigator}
options={{
tabBarLabel: 'Outfits',
}}
/>
<Tab.Screen
name="Chat"
component={Chat}
options={{
tabBarLabel: 'Chat',
}}
/>
</Tab.Navigator>
);
};
我想从
LookBookList
导航到
Wardrobe
,这就是我在
lookbook-list.js
文件中所做的。
<HeaderBar
backButton
leftClick={() => navigation.goBack()}
rightIcon={<HangerIcon />}
rightIconClick={() => {
navigation.navigate('Wardrobe', {openModal: true});
}}
/>
我的 Wardrobe.js 文件:
export const Wardrobe = ({route}) => {
console.log('route.params', route.params) // <== this is undefined ???
const {openModal} = route.params;
const navigation = useNavigation();
const [isVisible, setIsVisible] = useState(openModal);
我收到以下错误:
TypeError:undefined 不是对象(评估“route.params.openModal”)
通过查看您的代码,我认为问题在于您还可以从选项卡访问
WardrobeNavigator
,而不仅仅是通过导航。由于您的初始屏幕是
WardrobeNavigator
中的
Wardrobe
,并且由于您没有传递
initialParams
,因此在导航上设置参数之前会评估
route.params.openModal
。
为了防止它未定义并提供默认值,我们可以访问此语法:
const openModal = route.params?.openModal ?? false; // So false is the default value here
https://reactnavigation.org/docs/upgrading-from-4.x/#no-more-getparam
更新
它不起作用的原因是因为您正在更新
WardrobeNavigator
的参数,而不是
Wardrobe
。由于您想要将参数传递给嵌套屏幕,您可以执行以下操作:
navigation.navigate('Wardrobe', {
screen: 'Wardrobe',
params: { openModal: true },
})
因此,我使用 React 的
Context
API 解决了该问题。
在 wardrobe-context.js 中创建了一个上下文:
import React, {useState} from 'react';
export const WardrobeContext = React.createContext();
function WardrobeProvider(props) {
const [isAddItemModalOpen, setIsAddItemModalOpen] = useState(false);
return (
<WardrobeContext.Provider
value={{isAddItemModalOpen, setIsAddItemModalOpen}}>
{props.children}
</WardrobeContext.Provider>
);
}
export default WardrobeProvider;
在
navigation.js
文件中,使用
WardrobeProvider
包装了我的根导航堆栈:
export const RootNavigator = () => {
return (
<WardrobeProvider> // <== wrapper context provider
<RootStack.Navigator
screenOptions={{
headerShown: false
}}
initialRouteName='Main'
mode='modal'>
<RootStack.Screen name="Main" component={BottomTabNavigator} />
<RootStack.Screen name="Brands" component={BrandStores} />
<RootStack.Screen name="ItemScreen" component={ItemScreen} />
<RootStack.Screen name="CreateLook" component={CreateLookNavigator} />
<RootStack.Screen name="ShoppingList" component={ShoppingList} />
<RootStack.Screen name="StylistInstructions" component={ProfileNavigator} />
{/* add full screen appear as modal */}
</RootStack.Navigator>
</WardrobeProvider>
);
}
最后,在 Wardrobe.js 中:
import React, { useState, useEffect, useContext } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import { WardrobeContext } from '../../../context/wardrobe-context';
export const Wardrobe = () => {
const {isAddItemModalOpen, setIsAddItemModalOpen} = useContext(WardrobeContext);
const [isVisible, setIsVisible] = useState(false);
useFocusEffect(
React.useCallback(() => {
setIsVisible(isAddItemModalOpen) // <== set value here to open modal
return () => setIsAddItemModalOpen(false); // <== clear/reset the value on navigation change
}, [isAddItemModalOpen])
);
useFocusEffect
用于在
Wardrobe.js
屏幕聚焦时设置
isAddItemModalOpen
的
isVisible
状态的值,当屏幕未聚焦时,或者换句话说,当用户导航到另一个屏幕时,上下文中的值设置为 false。
从 lookbook-list.js 导航到 Wardrobe.js 更新上下文值:
import {WardrobeContext} from '../../../context/wardrobe-context';
<HeaderBar
backButton
leftClick={() => navigation.goBack()}
rightIcon={<HangerIcon />}
rightClick={() => {
wardrobeContext.setIsAddItemModalOpen(true); // <== update context here
navigation.navigate('Wardrobe'); // <== and then navigate
}}
/>
欢迎提出改进建议。谢谢