android 如何防止对案例进行不必要重组?

k4ymrczo  于 2023-06-27  发布在  Android
关注(0)|答案(1)|浏览(159)

我的NavHost中包含以下登录屏幕代码:

  1. @Composable
  2. fun LoginScreen(
  3. loginViewModel: LoginViewModel = koinViewModel(),
  4. navController: NavController,
  5. showSnackbar: (String, SnackbarDuration) -> Unit
  6. ) {
  7. Timber.i("LoginScreen recomposed")
  8. val uiState by loginViewModel.uiState.collectAsState()
  9. val ssnInput = loginViewModel.ssnInput
  10. ScreenContent(
  11. isLoading = uiState.isLoading,
  12. userInput = ssnInput.takeIf { it.length != SSN_LENGTH } ?: stringResource(id = R.string.saved_ssn, ssnInput.substring(0, 8)),
  13. onInputValueChanged = { loginViewModel.updateSsnUserInput(it) },
  14. onLogInClick = { loginViewModel.sendBankIdRequest() },
  15. isSsnInvalid = uiState.error is InputError,
  16. onKeyboardDone = { },
  17. onClearClick = { loginViewModel.clearInputText() }
  18. )
  19. uiState.error?.let {
  20. val messageId = when (it) {
  21. InputError -> R.string.invalid_personal_number_format
  22. BankIdFailed -> R.string.bankid_failed_check_ssn
  23. NetworkError -> R.string.bankid_internal_error
  24. GenericError -> R.string.bankid_failed
  25. NoGMSError -> R.string.no_gms_error
  26. }
  27. showSnackbar(stringResource(id = messageId), SnackbarDuration.Short)
  28. }
  29. }

在ScreenContent内部,我有一个Box,其中包含一个用作屏幕背景的Image,以及一个Column,其中包含组成屏幕其余部分的组件(标题文本,自定义文本字段,登录按钮,阅读更多可点击文本和徽标图像)。我创建了ScreenContent,这样我就可以在预览中使用它,因为它不支持ViewModels。
我遇到的问题是,每当我点击登录按钮,这反过来又调用loginViewModel.sendBankIdRequest(),uiState.error得到更新InputError作为值(这是我想要的)和LoginScreen得到重组的结果,这导致showSnackbar被调用,这是预期的行为时,点击按钮。然而,从这一点开始,每次调用onInputValueChanged lambda时,LoginScreen都会重新组合,每当我在文本字段中输入或删除一个字符时都会发生这种情况。这会导致每次由于重组而调用showSnackbar,即使uiState没有更改。
我该怎么解决这个问题?
我可以通过更新uiState并在每次更新文本输入值时将error设置为null来缓解这种情况,但我不认为这是一种解决方案。我想要的解决方案是除非uiState被更改,否则LoginScreen不会被重新组合。将userInput参数的值更改为loginViewModel.ssnInput而不在ssnInput中声明它也没有帮助。

2mbi3lxu

2mbi3lxu1#

我认为你可以通过将showSnackbar提取到副作用块中来解决它。
以下是官方文档副作用#launchedefect的示例

  1. LaunchedEffect(uiState.error) {
  2. showSnackbar(error, SnackbarDuration.Short)
  3. }

和错误提取逻辑应单独放置到ViewModel中

相关问题