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> ); }
错误原因
- 违反React Hook调用规则:React要求所有Hook必须在组件的顶层作用域调用,不能放在条件语句、循环或嵌套函数中。代码中将
useEffect放在if (userInfo)内部,导致当userInfo不存在时不会调用该Hook,存在时才调用,两次渲染的Hook调用数量不一致,触发错误。 - 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
相关产品推荐
相关产品推荐

