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

React Native集成Firebase谷歌登录时出现useMemo等错误求助

React Native + TypeScript + Firebase谷歌登录报错解决

问题背景

我是React Native新手,用TypeScript开发应用并集成Firebase谷歌登录,在XCode模拟器构建模式下运行时触发以下错误:

ERROR Warning: Invalid hook call. Hooks can only be called inside of
the body of a function component. This could happen for one of the
following reasons:

  1. You might have mismatching versions of React and the renderer
    (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app

ERROR TypeError: Cannot read property 'useMemo' of null, js engine:
hermes

ERROR Invariant Violation: "main" has not been registered. This can
happen if:

  • Metro (the local dev server) is run from the wrong folder. Check if
    Metro is running, stop it and restart it in the current project.
  • A module failed to load due to an error and
    AppRegistry.registerComponent wasn't called., js engine: hermes

我未使用useMemo Hook,试过Stack Overflow多种方案无效,相关代码如下:

index.ts

import { AppRegistry} from "react-native";
import { registerRootComponent } from "expo";
import App from "./App"

AppRegistry.registerComponent("App", () => App);
registerRootComponent(App);

App.tsx

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import LoginScreen from './src/components/LoginScreen';
import SignUpScreen from './src/components/SignUpScreen';
import ForgotPasswordScreen from './src/components/ForgotPassword';
import OtpScreen from './src/components/OtpScreen';

const Stack = createStackNavigator();

const App = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Login">
        <Stack.Screen name="Login" options={{headerShown: false}} component={LoginScreen} />
        <Stack.Screen name="SignUp" options={{headerShown: false}} component={SignUpScreen} />
        <Stack.Screen name="ForgotPassword" options={{headerShown: false}} component={ForgotPasswordScreen} />
        <Stack.Screen name="OTP" options={{headerShown: false}} component={OtpScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

export default App;

LoginScreen.tsx

import React, { useState, useEffect } from 'react';
import { View, Text, TextInput, StyleSheet, Button, TouchableOpacity } from 'react-native';
import { promptAsync, request, response } from '../firebase/firebase.sign-in.util';
import { LinearGradient } from 'expo-linear-gradient';
import { GoogleAuthProvider, signInWithCredential } from 'firebase/auth';
import {auth} from '../firebase/firebase.util';
const LoginScreen = ({ navigation }: { navigation: any }) => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');

    const handleLogin = () => {
        console.log('Login button pressed');
    };
    
    useEffect(()=>{
        if(response?.type == "success"){
            const {id_token} = response.params;
            const credential = GoogleAuthProvider.credential(id_token);
            signInWithCredential(auth, credential)
        }
    },[response])

    return (
        <LinearGradient
            colors={['#CACACA', '#053C4E']}
            start={{ x: 0, y: 0 }}
            end={{ x: 1, y: 1 }}
            style={styles.container}
        >
            <Text style={styles.title}>Login</Text>
            <TextInput
                style={styles.input}
                placeholder="Email"
                placeholderTextColor="#f6f6f6"
                value={email}
                onChangeText={setEmail}
            />
            <TextInput
                style={styles.input2}
                placeholder="Password"
                placeholderTextColor="#f6f6f6"
                secureTextEntry={true}
                value={password}
                onChangeText={setPassword}
            />
            <TouchableOpacity onPress={() => navigation.navigate('ForgotPassword')}>
                <Text style={styles.forgotPassword} >Forgot Password ?</Text>
            </TouchableOpacity>
            <TouchableOpacity style={styles.loginButton} onPress={() => promptAsync()}>
                <Text style={styles.loginButtonText}>Login</Text>
            </TouchableOpacity>
        </LinearGradient>
    );
};

解决方案

1. 修复index.ts的重复注册问题

Expo项目中无需同时调用AppRegistry.registerComponent和registerRootComponent,二者会导致注册冲突,保留Expo官方推荐的registerRootComponent即可:

// 修改后的index.ts
import { registerRootComponent } from "expo";
import App from "./App"

registerRootComponent(App);

2. 排查firebase.sign-in.util的Hook误用

错误提示的useMemo为空,大概率是../firebase/firebase.sign-in.util文件中存在Hook违规调用(比如在函数组件外部使用useMemo、useState等),或依赖库版本不兼容:

  • 检查该文件代码,确保所有Hook都在函数组件内部调用
  • 锁定React与React Native的匹配版本,执行:
    npm install react@18.2.0 react-native@0.72.0
    

3. 清理缓存并重启Metro

缓存异常会引发这类错误,执行以下命令清除缓存并重启服务:

# 清除Expo缓存
npx expo start --clear

# 或清除Metro缓存
rm -rf node_modules/.cache

4. 检查重复的React副本

执行命令查看是否存在多版本React:

npm ls react

若输出多个版本,删除依赖文件后重新安装:

rm -rf node_modules package-lock.json
npm install

5. 完善组件代码结构

确保LoginScreen组件标签完整闭合,避免渲染错误引发后续Hook调用异常。

内容的提问来源于stack exchange,提问作者Iseoluwa ariyibi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 05:13:15