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

React Hooks调用顺序错误求助:ScreenA组件报错修复咨询

解决React Hooks调用顺序错误警告

报错信息

ERROR Warning: React has detected a change in the order of Hooks called by ScreenA. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

当前代码功能可正常运行,但触发了Hooks调用顺序错误的警告,需修复后再扩展功能。

问题代码

import React, { useCallback, useEffect, useState } from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import { useNavigation } from '@react-navigation/native';
import { DancingScript_400Regular } from "@expo-google-fonts/dancing-script";
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';

export default function ScreenA({ route }) {

    const [appIsReady, setAppIsReady] = useState(false);

    useEffect(() => {
        async function prepare() {
            try {
                // Keep the splash screen visible while we fetch resources
                await SplashScreen.preventAutoHideAsync();

                // Pre-load fonts, make any API calls you need to do here
                await Font.loadAsync({ DancingScript_400Regular });

                // Artificially delay for two seconds to simulate a slow loading
                // experience. Please remove this if you copy and paste the code!
                await new Promise(resolve => setTimeout(resolve, 2000));
            } catch (e) {
                console.warn(e);
            } finally {
                // Tell the application to render
                setAppIsReady(true);
            }
        }

        prepare();
    }, []);

    const onLayoutRootView = useCallback(async () => {
        if (appIsReady) {
            // This tells the splash screen to hide immediately! If we call this after
            // `setAppIsReady`, then we may see a blank screen while the app is
            // loading its initial state and rendering its first pixels. So instead,
            // we hide the splash screen once we know the root view has already
            // performed layout.
            await SplashScreen.hideAsync();
        }
    }, [appIsReady]);

    if (!appIsReady) {
        return null;
    }

    const navigation = useNavigation();
    const onPressHandler = () => {
        // navigation.navigate('Screen_B', { itemName: 'Item from Screen A', itemID: 12 });
    }

    return (
        <View style={styles.body} onLayout={onLayoutRootView}>
            <Text style={styles.text}>
                Screen A
            </Text>
            <Pressable
                onPress={onPressHandler}
                style={({ pressed }) => ({ backgroundColor: pressed ? '#ddd' : '#0f0' })}
            >
                <Text style={styles.text}>
                    Go To Screen B
                </Text>
            </Pressable>
            <Text style={styles.text}>{route.params?.Message}</Text>
        </View>
    )

}

const styles = StyleSheet.create({
    body: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    text: {
        fontSize: 40,
        margin: 10,
        fontFamily: 'DancingScript_400Regular'
    }
})

解决方案

问题根源是useNavigation Hook被放在了条件判断语句之后。根据React Hooks规则,所有Hooks必须在组件的顶层作用域调用,不能放在条件、循环、嵌套函数或返回语句之后,确保组件每次渲染时Hooks的调用顺序完全一致。

修复方法:将const navigation = useNavigation();移到组件顶层,也就是if (!appIsReady) { return null; }之前。

修复后的代码

import React, { useCallback, useEffect, useState } from "react";
import { View, Text, StyleSheet, Pressable } from "react-native";
import { useNavigation } from '@react-navigation/native';
import { DancingScript_400Regular } from "@expo-google-fonts/dancing-script";
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';

export default function ScreenA({ route }) {

    const [appIsReady, setAppIsReady] = useState(false);
    // 移到组件顶层
    const navigation = useNavigation();

    useEffect(() => {
        async function prepare() {
            try {
                // Keep the splash screen visible while we fetch resources
                await SplashScreen.preventAutoHideAsync();

                // Pre-load fonts, make any API calls you need to do here
                await Font.loadAsync({ DancingScript_400Regular });

                // Artificially delay for two seconds to simulate a slow loading
                // experience. Please remove this if you copy and paste the code!
                await new Promise(resolve => setTimeout(resolve, 2000));
            } catch (e) {
                console.warn(e);
            } finally {
                // Tell the application to render
                setAppIsReady(true);
            }
        }

        prepare();
    }, []);

    const onLayoutRootView = useCallback(async () => {
        if (appIsReady) {
            // This tells the splash screen to hide immediately! If we call this after
            // `setAppIsReady`, then we may see a blank screen while the app is
            // loading its initial state and rendering its first pixels. So instead,
            // we hide the splash screen once we know the root view has already
            // performed layout.
            await SplashScreen.hideAsync();
        }
    }, [appIsReady]);

    if (!appIsReady) {
        return null;
    }

    const onPressHandler = () => {
        // navigation.navigate('Screen_B', { itemName: 'Item from Screen A', itemID: 12 });
    }

    return (
        <View style={styles.body} onLayout={onLayoutRootView}>
            <Text style={styles.text}>
                Screen A
            </Text>
            <Pressable
                onPress={onPressHandler}
                style={({ pressed }) => ({ backgroundColor: pressed ? '#ddd' : '#0f0' })}
            >
                <Text style={styles.text}>
                    Go To Screen B
                </Text>
            </Pressable>
            <Text style={styles.text}>{route.params?.Message}</Text>
        </View>
    )

}

const styles = StyleSheet.create({
    body: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    text: {
        fontSize: 40,
        margin: 10,
        fontFamily: 'DancingScript_400Regular'
    }
})

内容的提问来源于stack exchange,提问作者Jógvan Reinert Samuelsen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:10:14