渲染器比之前的渲染器有更多的钩子

Nil*_*r F 3 reactjs react-native react-navigation expo

我正在开发一个带有 bare expo 和 React Native 的项目。我已经在我的项目中实施了 firebase。登录和注册工作正常。我创建了一个身份验证检查,以便在用户登录时将其发送到另一个页面。尽管在尝试将用户发送到另一个页面时收到以下错误: 在此输入图像描述

这是我的 App.tsx:

import React, { useEffect, useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { ThemeProvider } from 'styled-components';
import {
    useFonts,
    Poppins_400Regular,
    Poppins_500Medium,
    Poppins_700Bold
} from '@expo-google-fonts/poppins';
import AppLoading from 'expo-app-loading';
import theme from './src/global/styles/theme';
import { NavigationContainer } from '@react-navigation/native';
import { AppRoutes } from './src/routes/app.routes';
import 'intl';
import 'intl/locale-data/jsonp/pt-BR';
import { SignIn } from './src/screens/SignIn';
import auth, { FirebaseAuthTypes } from '@react-native-firebase/auth';

export default function App() {

    const [ fontsLoaded ] = useFonts({
        Poppins_400Regular,
        Poppins_500Medium,
        Poppins_700Bold
    });
    const [user, setUser] = useState<FirebaseAuthTypes.User | null>(null);

    if (!fontsLoaded) {
        return <AppLoading />;
    }

    useEffect(() => {
        const subscriber = auth().onAuthStateChanged(setUser);
        return subscriber;
    }, []);

  return (
        <ThemeProvider theme={theme}>
            <StatusBar style="light"/>
            <NavigationContainer>
                { user ? <AppRoutes /> : <SignIn /> }
            </NavigationContainer>                      
        </ThemeProvider>
  );
}
Run Code Online (Sandbox Code Playgroud)

这是路线代码:

import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Dashboard } from '../screens/Dashboard';
import { Register } from '../screens/Register';
import { useTheme } from 'styled-components';
import { Platform } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import { Summary } from '../screens/Summary';
 
const { Navigator, Screen } = createBottomTabNavigator();

export function AppRoutes() {
    const theme = useTheme();

    return (
        <Navigator
            screenOptions={{
                headerShown: false,
                tabBarActiveTintColor: theme.colors.orange,
                tabBarInactiveTintColor: theme.colors.texts,
                tabBarLabelPosition: 'beside-icon',
                tabBarStyle: {
                    height: 88,
                    paddingVertical: Platform.OS === 'ios' ? 20 : 0
                }
            }}
        >
            <Screen 
                key="Listing"
                name="Listing"
                component={Dashboard}
                options={{
                    tabBarIcon: (({ size, color }) => 
                        <MaterialIcons 
                            name="format-list-bulleted"
                            size={size}
                            color={color}
                        /> 
                    )
                }}
            />
            <Screen
                key="Register"
                name="Register"
                component={Register}
                options={{
                    tabBarIcon: (({ size, color }) => 
                        <MaterialIcons 
                            name="attach-money"
                            size={size}
                            color={color}
                        /> 
                    )
                }}
            />
            <Screen 
                key="Summary"
                name="Summary"
                component={Summary}
                options={{
                    tabBarIcon: (({ size, color }) => 
                        <MaterialIcons 
                            name="pie-chart"
                            size={size}
                            color={color}
                        /> 
                    )
                }}
            />
        </Navigator>
    );
}
Run Code Online (Sandbox Code Playgroud)

Ter*_*rry 9

您的问题来自此块:

\n
if (!fontsLoaded) {\n    return <AppLoading />;\n}\n\nuseEffect(() => {\n    const subscriber = auth().onAuthStateChanged(setUser);\n    return subscriber;\n}, []);\n
Run Code Online (Sandbox Code Playgroud)\n

您在这里有条件地添加一个钩子,因为如果fontsLoaded计算结果为 false,您会更早返回渲染函数,并且useEffect钩子永远不会在第一次渲染时运行。但是,在后续尝试中,可能会加载字体,这会导致呈现不同数量的挂钩:因此会出现错误。

\n

基于react自己的“hooks规则”指南,强调我自己的:

\n
\n

不要\xe2\x80\x99t 在循环、条件或嵌套函数内调用 Hook。相反,在任何早期返回之前,始终在 React 函数的顶层使用 Hooks。通过遵循此规则,您可以确保每次渲染组件时都以相同的顺序调用 Hook。

\n
\n

要解决此问题,只需确保在任何路径之前定义所有挂钩即可return。在您的情况下,这是通过交换fontsLoaded支票来完成的:

\n
// Define all hooks before any return paths!\nuseEffect(() => {\n    const subscriber = auth().onAuthStateChanged(setUser);\n    return subscriber;\n}, []);\n\n\nif (!fontsLoaded) {\n    return <AppLoading />;\n}\n\nreturn (\n    <ThemeProvider theme={theme}>\n        <StatusBar style="light"/>\n        <NavigationContainer>\n            { user ? <AppRoutes /> : <SignIn /> }\n        </NavigationContainer>                      \n    </ThemeProvider>\n);\n
Run Code Online (Sandbox Code Playgroud)\n

当然,有时很难捕获这样的错误,尤其是在重型/复杂的组件中。如果您使用 eslint,则有一个名为 eslint 的插件eslint-plugin-react-hook,当发生这种情况时,它会导致 eslint 抛出警告/错误。

\n