首次点击触发API未等待响应,导致UserAuth数据未更新的问题解决
问题描述
点击事件调用checkUserforEmnail函数时,首次点击打印的userData为null,再次点击才能正常获取到数据。原因是API响应需要时间,代码未等待响应返回就直接使用了默认的null值;第二次点击时能获取到已更新的userData。
相关代码
UI层代码
var authViewModel: UserAuthViewModel = hiltViewModel() val userData by authViewModel.authUserData.collectAsState() fun checkUserforEmnail( sharedViewModel: SharedViewModel, userData: AuthResponseModel?, authViewModel : UserAuthViewModel, navController: NavController, textUser: String ) { Log.d("TAG", "checkUserforEmnail: ${userData}") if (userData == null){ authViewModel.getAuthUserDataEmail(textUser = textUser) Log.d("TAG", "checkUserforEmnail: ${userData}") } if(userData?.result?.user_exist == "USER_NOT_EXISTS"){ navController.navigate(NavScreens.REGISTRATION.route) } else if(userData?.result?.user_exist == "ACCOUNT_EXISTS") { sharedViewModel.addUsername(userName = textUser) navController.navigate(NavScreens.LOGIN.route) } else if(userData?.result?.user_exist == "ACCOUNT_NOT_VERIFIED"){ } }
ViewModel代码
@HiltViewModel class UserAuthViewModel @Inject constructor(private val repository: Repository) : ViewModel() { val authUserData: StateFlow<AuthResponseModel?> get() = repository.authUserData fun getAuthUserData(textUser: String, countryCode:String){ viewModelScope.launch { val result = repository.getAuthUserData(AuthUser(username = textUser, country_dial_code = countryCode)) } } fun getAuthUserDataEmail(textUser: String) { viewModelScope.launch { val result = repository.getAuthUserData(AuthUser(username = textUser)) } } }
解决方案
核心思路是不要在点击事件中同步判断异步返回的userData,而是通过观察StateFlow的状态变化来处理后续导航逻辑,同时添加加载状态避免重复请求:
1. 改造ViewModel,添加加载状态与状态更新逻辑
@HiltViewModel class UserAuthViewModel @Inject constructor(private val repository: Repository) : ViewModel() { // 自行维护MutableStateFlow,确保能主动更新状态 private val _authUserData = MutableStateFlow<AuthResponseModel?>(null) val authUserData: StateFlow<AuthResponseModel?> = _authUserData private val _isLoading = MutableStateFlow(false) val isLoading: StateFlow<Boolean> = _isLoading fun getAuthUserData(textUser: String, countryCode:String){ viewModelScope.launch { _isLoading.value = true try { val response = repository.getAuthUserData(AuthUser(username = textUser, country_dial_code = countryCode)) _authUserData.value = response // 请求成功后更新状态 } finally { _isLoading.value = false } } } fun getAuthUserDataEmail(textUser: String) { viewModelScope.launch { _isLoading.value = true try { val response = repository.getAuthUserData(AuthUser(username = textUser)) _authUserData.value = response // 请求成功后更新状态 } finally { _isLoading.value = false } } } // 处理完导航后重置状态,避免重复触发逻辑 fun resetAuthUserData() { _authUserData.value = null } }
2. 调整UI层逻辑,分离请求触发与状态处理
var authViewModel: UserAuthViewModel = hiltViewModel() val userData by authViewModel.authUserData.collectAsState() val isLoading by authViewModel.isLoading.collectAsState() val textUser = remember { mutableStateOf("") } // 绑定输入框的文本状态 // 点击事件仅负责触发API请求,不处理后续导航 fun onCheckEmailClick(sharedViewModel: SharedViewModel, navController: NavController) { if (isLoading || textUser.value.isBlank()) return // 防止重复点击或空输入 authViewModel.getAuthUserDataEmail(textUser.value) } // 用LaunchedEffect监听userData变化,自动处理导航 LaunchedEffect(userData) { userData?.let { data -> when(data.result?.user_exist) { "USER_NOT_EXISTS" -> navController.navigate(NavScreens.REGISTRATION.route) "ACCOUNT_EXISTS" -> { sharedViewModel.addUsername(textUser.value) navController.navigate(NavScreens.LOGIN.route) } "ACCOUNT_NOT_VERIFIED" -> { // 这里添加账号未验证的处理逻辑,比如弹窗提示用户验证 } } // 处理完成后重置状态,避免页面重组时重复触发导航 authViewModel.resetAuthUserData() } }
3. 关键说明
- 异步请求的结果通过
StateFlow通知UI,UI在状态更新时自动重组,此时处理导航逻辑就能拿到最新的userData - 加载状态
isLoading可以避免用户重复点击发起多次无效API请求 - 处理完导航后重置
authUserData,防止页面因其他原因重组时再次触发相同的导航逻辑
内容的提问来源于stack exchange,提问作者Priyanshu Raj
相关产品推荐
相关产品推荐

