React Native多useEffect异步问题求助:AsyncStorage与Axios调用异常
React Native异步问题:AsyncStorage与Axios API调用失败及状态未正确更新
需求
- 向两个独立API发起认证请求获取数据
- 在界面展示数据并附加自定义文本
- API请求需在请求头携带从AsyncStorage获取的login_token
当前问题
- 从AsyncStorage获取的login_token未正确同步到state
- API调用返回的数据未正确设置到state
- 导致API请求失败,渲染时出现undefined错误
错误原因分析
- 异步时序混乱:多个无依赖的useEffect同时执行,API调用时login_token、conversation_id等核心状态尚未初始化完成,请求携带空值或无效参数
- AsyncStorage处理不规范:getlogintoken使用回调式异步操作,无法保证状态更新完成后再执行API调用,且setLoading的位置错误
- useEffect依赖缺失:触发API调用的useEffect未监听login_token、conversation_id等关键状态,状态更新后不会重新发起请求
- 状态类型错误:currentuser从AsyncStorage获取的是JSON字符串,直接存入state后,后续JSON.parse会抛出异常
- welcome消息触发逻辑错误:初始化时的useEffect仅执行一次,conversation_details和ExpertProfile更新后不会重新触发消息生成
修复后的完整代码
import React, { FunctionComponent, useEffect, useCallback, useState} from 'react'; import { StyleSheet, View} from 'react-native'; import { GiftedChat } from 'react-native-gifted-chat'; import { RootStackParamList } from '../../navigators/RootStack'; import { StackScreenProps } from '@react-navigation/stack'; import { Convo_details, Send_Msg, Expert_Public_Profile } from '../../api/UserApi'; import Spinner from 'react-native-loading-spinner-overlay'; import AsyncStorage from '@react-native-async-storage/async-storage'; import uuid from 'react-native-uuid'; export type Props = StackScreenProps<RootStackParamList, "Chat">; const Chat: FunctionComponent<Props> = ({ navigation, route }) => { const [login_token, setlogin_token] = useState('') const [conversation_id, setconversation_id] = useState('') const [conversation_details, setconversation_details] = useState<Record<string, any>>({}) const [currentuser, setcurrentuser] = useState<Record<string, any>>({}) const [loading, setLoading] = useState(false); const [expertuid, setexpertuid] = useState('') const [ExpertProfile, setExpertProfile] = useState<Record<string, any>>({}) const [messages, setMessages] = useState([]); // 获取login_token useEffect(() => { const fetchLoginToken = async () => { try { const res = await AsyncStorage.getItem("login_token"); if (res) { setlogin_token(res); } else { alert("No login token found"); } } catch (err) { console.error("Failed to get login token:", err); alert("Failed to load authentication token"); } }; fetchLoginToken(); }, []); // 获取路由参数 useEffect(() => { if (route.params?.length >= 2) { setconversation_id(route.params[0]); setexpertuid(route.params[1]); } }, [route.params]); // 获取当前用户 useEffect(() => { const fetchCurrentUser = async () => { try { const res = await AsyncStorage.getItem("currentuser"); if (res) { setcurrentuser(JSON.parse(res)); } else { alert("Current user not found"); } } catch (err) { console.error("Failed to get current user:", err); alert("Failed to load current user"); } }; fetchCurrentUser(); }, []); // 发起API请求(依赖token和参数就绪) useEffect(() => { if (!login_token || !conversation_id || !expertuid) return; const fetchData = async () => { setLoading(true); try { // 并行请求两个API const [convoRes, profileRes] = await Promise.all([ Convo_details(conversation_id, login_token), Expert_Public_Profile(expertuid, login_token) ]); if (convoRes.status === 200) { setconversation_details(convoRes.data); } else { alert(`Get conversation details failed: ${convoRes.data.message}`); } if (profileRes.status === 200) { setExpertProfile(profileRes.data); } else { alert(`Get expert profile failed: ${profileRes.data.message}`); } } catch (error: any) { console.error("API request failed:", error); alert(`Data loading failed: ${error.message}`); } finally { setLoading(false); } }; fetchData(); }, [login_token, conversation_id, expertuid]); // 生成欢迎消息(依赖数据就绪) useEffect(() => { if (Object.keys(conversation_details).length === 0 || Object.keys(ExpertProfile).length === 0 || Object.keys(currentuser).length === 0) return; try { const welcomeMessages = [ { _id: uuid.v4().toString(), conversation_id: conversation_details.conversation_id, created_at: new Date(), from: conversation_details.recipient.user_uid, type: "TEXT", text: `About Me - ${ExpertProfile.bio}`, user: { _id: conversation_details.recipient.user_uid } }, { _id: uuid.v4().toString(), conversation_id: conversation_details.conversation_id, created_at: new Date(), from: conversation_details.recipient.user_uid, type: "TEXT", text: `My name is ${conversation_details.recipient.name}`, user: { _id: conversation_details.recipient.user_uid } }, { _id: uuid.v4().toString(), conversation_id: conversation_details.conversation_id, created_at: new Date(), from: conversation_details.recipient.user_uid, type: "TEXT", text: `Hi ${currentuser.full_name}`, user: { _id: conversation_details.recipient.user_uid } } ]; setMessages(prev => GiftedChat.append(prev, welcomeMessages)); } catch (error) { console.error("Failed to set welcome messages:", error); } }, [conversation_details, ExpertProfile, currentuser]); const onSend = useCallback(async (messages = []) => { if (!login_token || !conversation_id) { alert("Missing authentication token or conversation ID"); return; } setMessages(prev => GiftedChat.append(prev, messages)); const data = { conversation_id, message_type: "TEXT", body: messages[0].text }; try { const res = await Send_Msg(data, login_token); if (res.status === 200) { console.log("Message sent successfully:", res.data); } else { alert(`Send message failed: ${res.data.message}`); } } catch (error: any) { console.error("Send message error:", error); alert(`Send message failed: ${error.message}`); } }, [login_token, conversation_id]); return ( <View style={styles.maincontainer}> <Spinner visible={loading} textContent={'Loading...'} textStyle={{ color: '#FFF' }} /> <GiftedChat messages={messages} onSend={onSend} user={{ _id: currentuser.user_uid || '' }} isTyping={false} scrollToBottom={true} showAvatarForEveryMessage={true} renderAvatar={() => null} /> </View> ); }; export default Chat; const styles = StyleSheet.create({ maincontainer: { flex: 1, }, });
核心修复说明
- 异步时序控制:所有AsyncStorage操作改用async/await,确保状态更新完成后再执行后续逻辑;API请求使用Promise.all并行发起,提升加载效率
- 依赖项修正:API请求和欢迎消息的useEffect添加必要依赖,仅当token、参数、数据就绪时才执行
- 状态类型处理:currentuser获取后直接解析JSON存入state,避免后续解析异常
- 错误处理优化:统一try/catch捕获异步操作异常,提供更清晰的错误提示
- loading状态管理:使用finally块确保请求完成后关闭loading,避免状态残留
- 移除硬编码:替换硬编码的conversation_id为state中的动态值,保证数据一致性
内容的提问来源于stack exchange,提问作者Tanmay Gurav
相关产品推荐
相关产品推荐

