我收到此错误:
Error: Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app, so this was probably an error. If this was intentional, pass 'independent={true}' explicitly. Note that this will make the child navigators disconnected from the parent and you won't be able to navigate between them.
at BaseNavigationContainer (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:71247:13)
at renderWithHooks (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:24954:24)
at updateForwardRef (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:27528:26)
at beginWork (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:29575:22)
at HTMLUnknownElement.callCallback (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:14550:20)
at Object.invokeGuardedCallbackDev (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:14594:22)
at invokeGuardedCallback (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:14651:37)
at beginWork$1 (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:34520:13)
at performUnitOfWork (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:33768:18)
at workLoopSync (entry.bundle?platform=web&dev=true&hot=false&lazy=true&transform.routerRoot=app&resolver.environment=client&transform.environment=client:33691:11)
Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
我不确定我做错了什么。
这是我的存储库的结构。
这是 navigationTypes.tsx
// app/navigationTypes.ts
export type RootStackParamList = {
HomeScreen: undefined; // HomeScreen doesn't expect any parameters
QRCodeScannerScreen: undefined; // QRCodeScannerScreen doesn't expect any parameters
};
这是app/index.tsx:
import React from 'react';
import { ImageBackground, Image, StyleSheet, View, TouchableOpacity, Text } from 'react-native';
import Icon from 'react-native-vector-icons/Ionicons';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { RootStackParamList } from './navigationTypes'; // Adjust path if necessary
type HomeScreenNavigationProp = NativeStackNavigationProp<RootStackParamList, 'HomeScreen'>;
export default function HomeScreen() {
const navigation = useNavigation<HomeScreenNavigationProp>();
return (
<ImageBackground
source={require('@/assets/images/background.png')}
style={styles.background}
imageStyle={styles.backgroundImage}
>
<View style={styles.container}>
<View style={styles.headerContainer}>
<TouchableOpacity style={styles.settingsIcon} onPress={() => alert('Settings pressed!')}>
<Icon name="settings-outline" size={30} color="#fff" />
</TouchableOpacity>
<Image
source={require('@/assets/images/logo_tokenchamp_white.png')}
style={styles.tokenChampLogo}
/>
<TouchableOpacity style={styles.mapIcon} onPress={() => alert('Map pressed!')}>
<Icon name="map-outline" size={30} color="#fff" />
</TouchableOpacity>
</View>
<Text style={styles.headerText}>Coupons</Text>
<View style={styles.whiteRectangle} />
<View style={styles.balanceContainer}>
<Text style={styles.headerText}>Balances</Text>
<View style={styles.balanceRow}>
<Text style={styles.balanceText}>TokenChamp</Text>
<TouchableOpacity style={styles.connectButton} onPress={() => alert('Add Funds pressed!')}>
<Text style={styles.connectButtonText}>Add Funds</Text>
</TouchableOpacity>
</View>
<View style={styles.balanceRow}>
<Text style={styles.balanceText}>AVAX</Text>
<TouchableOpacity style={styles.connectButton} onPress={() => alert('Connect Wallet pressed!')}>
<Text style={styles.connectButtonText}>Connect Wallet</Text>
</TouchableOpacity>
</View>
<View style={styles.balanceRow}>
<Text style={styles.balanceText}>USD</Text>
<TouchableOpacity style={styles.connectButton} onPress={() => alert('Connect Bank pressed!')}>
<Text style={styles.connectButtonText}>Connect Bank</Text>
</TouchableOpacity>
</View>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity
style={styles.playButton}
onPress={() => navigation.navigate('QRCodeScannerScreen')}
>
<Text style={styles.playButtonText}>Play Now</Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
);
}
const styles = StyleSheet.create({
background: {
flex: 1,
},
backgroundImage: {
width: '100%',
height: '100%',
resizeMode: 'cover',
},
container: {
flex: 1,
padding: 20,
},
headerContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
},
settingsIcon: {
padding: 10,
},
tokenChampLogo: {
width: 200,
height: 100,
resizeMode: 'contain',
},
mapIcon: {
padding: 10,
},
whiteRectangle: {
backgroundColor: '#fff',
height: 180, // Height of the white rectangle
marginBottom: 20,
width: '100%',
},
balanceContainer: {
marginBottom: 100,
},
headerText: {
color: '#fff',
fontSize: 24,
fontWeight: 'bold',
marginBottom: 20, // Spacing between header and rows
},
balanceRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
balanceText: {
color: '#fff',
fontSize: 18,
flex: 1, // Allow text to take up available space
},
connectButton: {
backgroundColor: '#ff0099',
paddingVertical: 10,
borderRadius: 30,
width: 150, // Fixed width for buttons
},
connectButtonText: {
color: '#fff',
fontSize: 14,
textAlign: 'center',
},
buttonContainer: {
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
bottom: 20,
left: 0,
right: 0,
paddingHorizontal: 20, // Padding to ensure the button doesn't touch the screen edges
},
playButton: {
backgroundColor: '#fff',
paddingVertical: 20,
borderRadius: 30,
width: '100%', // Full width
alignItems: 'center', // Center text within button
shadowColor: '#000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 6,
},
playButtonText: {
color: '#9d4edd',
fontSize: 20,
fontWeight: 'bold',
},
});
这是app/_layout.tsx:
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NavigationContainer } from '@react-navigation/native';
import * as SplashScreen from 'expo-splash-screen';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
import HomeScreen from './index';
import QRCodeScannerScreen from './(tabs)/QRCodeScannerScreen';
import { RootStackParamList } from './navigationTypes';
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
const Stack = createNativeStackNavigator<RootStackParamList>();
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<NavigationContainer>
<Stack.Navigator initialRouteName="HomeScreen">
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
options={{ headerShown: false }} // Hide header if you prefer
/>
<Stack.Screen
name="QRCodeScannerScreen"
component={QRCodeScannerScreen}
options={{ title: 'Scan QR Code' }}
/>
</Stack.Navigator>
</NavigationContainer>
</ThemeProvider>
);
}
这是应用程序/(选项卡)/QRCodeScannerScreen.tsx:
// /app/(tabs)/QRCodeScannerScreen.js
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { Camera } from 'expo-camera';
import { BarCodeScanner } from 'expo-barcode-scanner';
export default function QRCodeScannerScreen({ }) {
const [hasPermission, setHasPermission] = React.useState<boolean | null>(null);
const [scanned, setScanned] = React.useState<boolean>(false);
React.useEffect(() => {
(async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }: { type: string; data: string }) => {
setScanned(true);
alert(`Bar code with type ${type} and data ${data} has been scanned!`);
};
if (hasPermission === null) {
return <View style={styles.container}><Text>Requesting for camera permission</Text></View>;
}
if (hasPermission === false) {
return <View style={styles.container}><Text>No access to camera</Text></View>;
}
return (
<View style={styles.container}>
<Text style={styles.instructionText}>Scan QR codes to proceed:</Text>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && (
<TouchableOpacity
style={styles.button}
onPress={() => setScanned(false)}
>
<Text style={styles.buttonText}>Scan Again</Text>
</TouchableOpacity>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
},
instructionText: {
fontSize: 18,
marginBottom: 20,
},
button: {
backgroundColor: '#ff0099',
padding: 15,
borderRadius: 5,
marginTop: 20,
},
buttonText: {
color: '#fff',
fontSize: 16,
},
});
我的目标是,当您单击“立即播放”时,它会更改为 QRCodeScannerScreen 视图,其中有...QR 码扫描仪/激活您的相机。
我可以在错误中读到我已经嵌套了 NavigationContainers,但我的大脑已经崩溃了,我不知道如何解决这个问题。
我认为你将 React 导航实现与 Expo 路由器实现混淆了。
使用 expo router,可以为您处理许多样板文件,例如必须添加 NavigationContainer。该错误来自 expo router 和您的代码都添加了一个。
我还建议确保您从 expo-router 导入,而不是对堆栈等进行反应导航。