React Native / Expo NavigationContainer 问题

问题描述 投票:0回答:1

我收到此错误:

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

我不确定我做错了什么。

这是我的存储库的结构。

enter image description here

这是 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-native expo
1个回答
0
投票

我认为你将 React 导航实现与 Expo 路由器实现混淆了。

使用 expo router,可以为您处理许多样板文件,例如必须添加 NavigationContainer。该错误来自 expo router 和您的代码都添加了一个。

我还建议确保您从 expo-router 导入,而不是对堆栈等进行反应导航。

© www.soinside.com 2019 - 2024. All rights reserved.