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

React Native中Rendered more hooks错误的原因与解决方法

React Native 组件「Rendered more hooks than during the previous render」错误分析与修复

问题场景

开发HomeScreen组件时,计划通过useEffect调用API,期间将loading状态设为true,获取响应或遇到错误后设为false,但运行时触发以下错误:

Error: Rendered more hooks than during the previous render

相关代码如下:

function HomeScreen({ navigation }) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const { authToken, logInfo, userData } = useContext(AuthContext);

  const [token, setToken] = authToken;
  const [isloggedIn, setIsLoggedIn] = logInfo;
  const [userInfo, setUserInfo] = userData;

  if (userInfo) {
    useEffect(() => {
      const isUserCreatedProfile = async (loggedInUserInfo, token) => {
        try {
          const apiHeaders = {
            "User-Agent":
              "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0",
            Accept: "application/json, text/plain, */*",
            token: token,
          };

          await axios
            .get(`${baseUrl}/api/user?email=${loggedInUserInfo["email"]}`, {
              headers: apiHeaders,
            })
            .then((res) => {
              console.log(res);
            })
            .catch((err) => {
              console.log(err);
              navigation.replace("CreateProfile", {
                name: loggedInUserInfo["custom:Name"],
                email: loggedInUserInfo["email"],
              });
            });
        } catch (err) {
          console.log(err);
        }
      };
      setLoading(true);
      isUserCreatedProfile(userInfo, token);
      setLoading(false);
    }, []);
  }

  return (
    <SafeAreaView style={styles.container}>
      {loading ? (
        <ActivityIndicator color={colors.BorderColor} size="large" />
      ) : (
        <Pressable
          style={{ alignItems: "center", marginTop: 300 }}
          onPress={async () => {
            await AsyncStorage.removeItem("token");
            console.log(
              "token after delete:",
              await AsyncStorage.getItem("token"),
            );
            setIsLoggedIn(false);
            await Auth.signOut();
            setToken(null);
            setUserInfo(null);
          }}
        >
          <Text style={styles.buttonTextStyle}>Logout</Text>
        </Pressable>
      )}
    </SafeAreaView>
  );
}

错误原因

  1. 违反React Hook调用规则:React要求所有Hook必须在组件的顶层作用域调用,不能放在条件语句、循环或嵌套函数中。代码中将useEffect放在if (userInfo)内部,导致当userInfo不存在时不会调用该Hook,存在时才调用,两次渲染的Hook调用数量不一致,触发错误。
  2. loading状态逻辑错误:当前setLoading(false)是同步执行的,会在API请求完成前就将loading设为false,无法正确展示加载状态。

修复方案

步骤1:将Hook移到组件顶层

把useEffect从条件判断中移出,放在组件的顶层作用域内,确保每次渲染时Hook的调用顺序和数量一致。

步骤2:调整依赖数组

在useEffect的依赖数组中添加userInfo、token和navigation,确保当这些值变化时,useEffect能重新执行,保证数据的一致性。

步骤3:修正loading状态逻辑

在异步函数开始时设置loading为true,并在请求完成(无论成功或失败)的finally块中设置loading为false,确保加载状态正确更新。

步骤4:优化异步请求写法

避免await和.then()混用,统一使用async/await写法,让代码更简洁易读。

修正后的代码

function HomeScreen({ navigation }) {
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const { authToken, logInfo, userData } = useContext(AuthContext);

  const [token, setToken] = authToken;
  const [isloggedIn, setIsLoggedIn] = logInfo;
  const [userInfo, setUserInfo] = userData;

  // 移到组件顶层,确保每次渲染都调用该Hook
  useEffect(() => {
    // 当userInfo不存在时直接返回,不执行API请求
    if (!userInfo) {
      setLoading(false);
      return;
    }

    const isUserCreatedProfile = async () => {
      try {
        setLoading(true);
        const apiHeaders = {
          "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0",
          Accept: "application/json, text/plain, */*",
          token: token,
        };

        // 统一使用async/await写法,避免混用then
        const res = await axios.get(`${baseUrl}/api/user?email=${userInfo["email"]}`, {
          headers: apiHeaders,
        });
        console.log(res);
      } catch (err) {
        console.log(err);
        navigation.replace("CreateProfile", {
          name: userInfo["custom:Name"],
          email: userInfo["email"],
        });
      } finally {
        // 请求完成后无论结果如何都关闭loading
        setLoading(false);
      }
    };

    isUserCreatedProfile();
  }, [userInfo, token, navigation]); // 添加依赖,确保值变化时重新执行

  return (
    <SafeAreaView style={styles.container}>
      {loading ? (
        <ActivityIndicator color={colors.BorderColor} size="large" />
      ) : (
        <Pressable
          style={{ alignItems: "center", marginTop: 300 }}
          onPress={async () => {
            await AsyncStorage.removeItem("token");
            console.log(
              "token after delete:",
              await AsyncStorage.getItem("token"),
            );
            setIsLoggedIn(false);
            await Auth.signOut();
            setToken(null);
            setUserInfo(null);
          }}
        >
          <Text style={styles.buttonTextStyle}>Logout</Text>
        </Pressable>
      )}
    </SafeAreaView>
  );
}

内容的提问来源于stack exchange,提问作者Harsh Mishra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:05:22