You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Instagram克隆项目启动报错:找不到galleryImage

React Native/Expo 应用启动报错排查求助

报错信息

[Error]
Invariant Violation: Failed to call into JavaScript module method AppRegistry.runApplication(). Module has not been registered as callable. Registered callable JavaScript modules (n = 11): Systrace, JSTimers, HeapCapture, SamplingProfiler, RCTLog, RCTDeviceEventEmitter, RCTNativeAppEventEmitter, GlobalPerformanceLogger, JSDevSupportModule, HMRClient, RCTEventEmitter.
        A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:null in reportException
...(省略重复栈信息)

ReferenceError: Can't find variable: galleryImage
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:null in reportException
...(省略重复栈信息)

怀疑有问题的代码片段

第一段(FormikPostUploader.js)

import { View, Text, Image, TextInput} from 'react-native';
import * as React from 'react'
import * as Yup from 'yup';
import { Field, Form, Formik, FormikProps } from 'formik'
import { Button, Divider } from 'react-native-elements';
import { useNavigation } from '@react-navigation/native';
import AddPost, {cameraImage, galleryImage} from './AddPost';

const PLACEHOLDER_IMG = require('../../assets/defImg.png')

const uploadPostSchema = Yup.object().shape({
  imgUrl: Yup.string().url().required('A Image is required'),
  caption: Yup.string().max(2200, 'Caption cannot exceed character limit.')
})

const FormikPostUploader = () => {
  const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
  const navigation = useNavigation()
  
  return (
    <Formik 
    initialValues={{imageUrl: '', caption:''}} 
    onSubmit = {values => console.log(values)} 
    validationSchema = {uploadPostSchema}
    validateOnMount = {true}>
      {({
        handleBlur, 
        handleChange, 
        handleSubmit, 
        values, 
        errors, 
        isvalid}) => (
      <>
      <View style = {{margin:20, justifyContent:'space-between', flexDirection:'row'}}>
      <Image source = { thumbnailUrl }/>
        <View style = {{flex:1, margin:15}}>
        <TextInput 
          placeholder='Write a caption...'
          placeholderTextColor='gray' 
          multiline = {true}
          style = {{color:'white', fontSize:17}} 
          onChangeText = {handleChange('caption')}
          onBlur = {handleBlur('caption')}
          value = {values.caption}/>
        </View>
      </View>
        <Divider width={0.5} orientation='vertical'/>
        <View style = {{alignItems:'center', marginTop:15}}>
          <Text style = {{fontSize:18, fontWeight: 'bold', color: 'white'}}>Choose Image</Text>
        </View>
        <View style = {{justifyContent: 'space-between', flexDirection:'row', margin:30}}>
          <Button title='Choose Image' onPress={() => navigation.navigate('AddPost')}/>
          <Button onPress={handleSubmit} title = 'Post' disabled={isvalid}/>
        </View>
      </>
      )}
    </Formik>
  )
}

export default FormikPostUploader

第二段(AddPost.js)

import { View, Text, Image, StyleSheet, Button } from 'react-native'
import React, {useState, useEffect} from 'react'
import { Camera } from 'expo-camera'
import * as ImagePicker from 'expo-image-picker'
import { TouchableOpacity } from 'react-native'
import { useNavigation } from '@react-navigation/native'

const Header = () =>{ 
    const navigation = useNavigation()
    return (
    <View style = {styles.headerContainer}>
        <TouchableOpacity style = {{marginBottom:15}} onPress={() => navigation.goBack()}>
            <Image source = {require('../../assets/back.png')} style = {styles.icon}/>
        </TouchableOpacity>
        <Text style={{color:'white', fontWeight: '700', fontSize:20, marginRight: 25, marginTop:35, marginBottom:15}}>New Post</Text>
        <Text>      </Text>
    </View>
)}

export default function AddPost() {
    const [hasCameraPermission, setHasCameraPermission] = useState(null)
    const [hasGalleryPermission, setHasGalleryPermission] = useState(null)
    const [camera, setCamera] = useState(null)
    const [image, setImage] = useState(null)
    const [type, setType] = useState(Camera.Constants.Type.back)

    const navigation = useNavigation()

    const takePicture = async() => {
        if (camera) {
            const data = await camera.takePictureAsync(null)
            const cameraImage = data.uri
            setImage(cameraImage)
        }
    }

    const pickImage = async () => {
        let result = await ImagePicker.launchImageLibraryAsync({
            mediaTypes: ImagePicker.MediaTypeOptions.Images,
            allowsEditing: true,
            aspect: [1, 1],
            quality: 1
        })

        console.log(result)
        if (!result.canceled) {
            const galleryImage = result.assets[0].uri
            setImage(galleryImage)
        }
    }

    useEffect(() => {
        (async() => {
            const cameraStatus = await Camera.requestCameraPermissionsAsync()
            setHasCameraPermission(cameraStatus.status === 'granted')
            
            const galleryStatus = await ImagePicker.requestCameraRollPermissionsAsync()
            setHasGalleryPermission(galleryStatus.status === 'granted')
        })()
    }, [])
    

    if (hasCameraPermission === null || hasGalleryPermission === false) {
        return <View/>
    }
    if (hasCameraPermission === false || hasGalleryPermission === false){
        return <Text>Enable access for Camera to proceed</Text>
    }
    return (
        <View style={{ flex: 1}}>
            <Header/>
            <Camera 
                ref = {ref => setCamera(ref)}
                style={styles.fixedRatio} 
                type={type} 
                ratio={'1:1'}/>
            
            <View style = {styles.cameraContainer}>
                {image  &&  <TouchableOpacity onPress={() => navigation.navigate('NewPostScreen')}>
                                <Image source={{uri: image}} style = {{height:80, width:80, borderRadius:200}}/>
                            </TouchableOpacity>}
                
            <TouchableOpacity onPress={() => {setType(type === Camera.Constants.Type.back ? Camera.Constants.Type.front : Camera.Constants.Type.back)}}>
                <Image source={require('../../assets/turn-camera.png')} style = {{width:70, height:70, margin: 15}}/>
            </TouchableOpacity>

            <TouchableOpacity onPress={() => takePicture()}>
                <Image source={require('../../assets/camera-shutter.png')} style = {{width:70, height:70, marginRight:60}}/>
            </TouchableOpacity>

            <Button title='Gallery' onPress={() => pickImage()}/>
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    cameraContainer: {
        flex:0.15,
        backgroundColor: 'black',
        flexDirection:'row',
        justifyContent: 'space-between',
        alignItems: 'center',

    },
    fixedRatio: {
        flex:0.99,
        aspectRatio:0.9
    },
    headerContainer: {
        flexDirection: 'row',
        justifyContent: 'space-between',
        alignItems: 'center',
        backgroundColor:'black'
    },
    icon: {
        width:25,
        height: 25,
        marginHorizontal:15,
        marginTop:30,
    },
    iconContainer: {
        flexDirection:'row',
        justifyContent: 'space-between',
        height:50,
    },
})

module.exports = {galleryImage, cameraImage}

已尝试的解决方法

  • 清理缓存
  • 执行yarn install

问题排查与修复方案

核心问题分析

报错中ReferenceError: Can't find variable: galleryImage是触发后续AppRegistry错误的根源:

  1. galleryImage和cameraImage是takePicture、pickImage函数内部的局部变量,仅在函数作用域内有效,无法被外部访问。
  2. 你试图通过module.exports = {galleryImage, cameraImage}导出这两个变量,但外部模块加载时找不到对应的全局变量,直接抛出引用错误,中断了React Native初始化流程,进而引发AppRegistry.runApplication()错误。

修复步骤

步骤1:修复AddPost.js的图片传递逻辑

通过导航传参传递选中的图片地址,替代错误的导出方式:

  1. 修改AddPost.js中缩略图的跳转逻辑,携带图片参数:
{image  &&  <TouchableOpacity onPress={() => navigation.navigate('NewPostScreen', { selectedImage: image })}>
                <Image source={{uri: image}} style = {{height:80, width:80, borderRadius:200}}/>
            </TouchableOpacity>}
  1. 删除AddPost.js末尾错误的导出语句:
// 删掉此行
module.exports = {galleryImage, cameraImage}

步骤2:修复FormikPostUploader.js的导入与状态更新

  1. 修正错误的导入,移除无效的变量导入:
// 原代码:import AddPost, {cameraImage, galleryImage} from './AddPost';
import AddPost from './AddPost';
  1. 补充useState和useRoute的导入:
// 原代码:import * as React from 'react'
import React, { useState, useEffect } from 'react'
// 补充useRoute导入
import { useNavigation, useRoute } from '@react-navigation/native';
  1. 监听路由参数变化,更新缩略图状态:
const FormikPostUploader = () => {
  const [thumbnailUrl, setThumbnailUrl] = useState(PLACEHOLDER_IMG)
  const navigation = useNavigation()
  const route = useRoute()

  // 监听参数变化,更新缩略图
  useEffect(() => {
    if (route.params?.selectedImage) {
      setThumbnailUrl({ uri: route.params.selectedImage })
    }
  }, [route.params])
  
  // ... 其余代码不变
}

步骤3:修复表单验证与按钮逻辑

  1. 统一表单字段名,validationSchema与initialValues字段保持一致:
const uploadPostSchema = Yup.object().shape({
  // 原字段名imgUrl改为imageUrl,匹配initialValues
  imageUrl: Yup.string().required('A Image is required'),
  caption: Yup.string().max(2200, 'Caption cannot exceed character limit.')
})

注:本地图片URI不是标准URL,所以移除.url()验证规则

  1. 修正按钮禁用逻辑,取反验证状态:
// 原代码:disabled={isvalid}
<Button onPress={handleSubmit} title = 'Post' disabled={!isValid}/>

额外优化建议

  • 可以考虑使用React Context或Redux实现全局状态管理,更方便地跨组件传递图片数据。
  • 用Expo的ImageManipulator处理图片尺寸、格式后再传递,提升性能和兼容性。

内容的提问来源于stack exchange,提问作者Pranav J

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 21:20:27