【问题标题】:Composable is recomposing endlessly after flow collectComposable 在流收集后不断重组
【发布时间】:2022-11-26 20:48:10
【问题描述】:

在流收集并导航到新屏幕后,我的可组合项会无休止地重新组合。 我不明白为什么。

我正在使用 Firebase 通过电子邮件和密码进行身份验证。

我不得不放置一些 Log.i 来测试我的功能和我的可组合项,是的,我的主要可组合项 (SignUp) 在导航后不断地重新组合。

视图模型

// Firebase auth
private val _signUpState = mutableStateOf<Resources<Any>>(Resources.success(false))
val signUpState: State<Resources<Any>> = _signUpState

fun firebaseSignUp(email: String, password: String) {
    job = viewModelScope.launch(Dispatchers.IO) {
        firebaseAuth.firebaseSignUp(email = email, password = password).collect {
            _signUpState.value = it
            Log.i("balito", "polipop")
        }
    }
}

fun stop() {
    job?.cancel()
}

报名

@Composable
fun SignUp(
    navController: NavController,
    signUpViewModel: SignUpViewModel = hiltViewModel()
) {
    val localFocusManager = LocalFocusManager.current
    Log.i("salut", "salut toi")
    Column(
        modifier = Modifier
            .fillMaxSize()
            .systemBarsPadding()
            .padding(16.dp)
            .background(color = PrimaryColor)
    ) {
        BackButton(navController = navController)
        Spacer(modifier = Modifier.height(30.dp))
        Text(
            text = stringResource(id = R.string.sinscrire),
            fontFamily = visby,
            fontWeight = FontWeight.SemiBold,
            fontSize = 28.sp,
            color = Color.White
        )
        Spacer(modifier = Modifier.height(2.dp))
        Text(
            text = stringResource(R.string.prenez_votre_sante_en_main),
            fontFamily = visby,
            fontWeight = FontWeight.SemiBold,
            fontSize = 20.sp,
            color = Grey
        )
        Spacer(modifier = Modifier.height(20.dp))
        Email(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
        Spacer(modifier = Modifier.height(16.dp))
        Password(signUpViewModel = signUpViewModel, localFocusManager = localFocusManager)
        Spacer(modifier = Modifier.height(30.dp))
        Button(value = stringResource(R.string.continuer), type = Type.Valid.name) {
            localFocusManager.clearFocus()
            signUpViewModel.firebaseSignUp(signUpViewModel.emailInput.value, signUpViewModel.passwordInput.value)
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(value = stringResource(R.string.inscription_avec_google), type = Type.Other.name) {

        }
        Spacer(modifier = Modifier.weight(1f))
        Box(
            modifier = Modifier
                .fillMaxWidth(),
            contentAlignment = Alignment.Center
        ) {
            ClickableTextInfo(stringResource(id = R.string.deja_un_compte_se_connecter), onClick = {})
        }
    }
    Response(navController = navController, signUpViewModel = signUpViewModel)
    DisposableEffect(key1 = signUpViewModel.signUpState.value == Resources.success(true)) {
        onDispose {
            signUpViewModel.stop()
            Log.i("fin", "fin")
        }
    }
}

@Composable
private fun Response(
    navController: NavController,
    signUpViewModel: SignUpViewModel
) {
    when (val response = signUpViewModel.signUpState.value) {
        is Resources.Loading<*> -> {
            //WaitingLoaderProgress(loading = true)
        }
        is Resources.Success<*> -> {
            response.data.also {
                Log.i("lolipop", "lolipopi")
                if (it == true) {
                    navController.navigate(Screen.SignUpConfirmation.route)
                }
            }
        }
        is Resources.Failure<*> -> {
//            response.throwable.also {
//                Log.d(TAG, it)
//            }
        }
    }
}

【问题讨论】:

  • 我对 Compose 不是很熟悉,但您可以从可组合项中调用 firebaseSignUp()。这是否意味着您每次重新组合都会开始一个新的注册过程?
  • @broot 它实际上看起来像一个onClick 回调,所以这部分应该没问题。但我更喜欢在回调的情况下使用命名参数来明确说明。

标签: android kotlin android-jetpack-compose kotlin-flow compose-recomposition


【解决方案1】:

在导航过渡期间,由于动画会发生多次重组,并且您在每次重组时调用navController.navigate

您不应直接从可组合构建器中产生副作用或更改状态,因为这将在每次重组时执行,这在动画等情况下是不会发生的。

相反,您应该使用side effects。在您的情况下,应使用 LaunchedEffect

if (response.data) {
    LaunchedEffect(Unit) {
        Log.i("lolipop", "lolipopi")
        navController.navigate(Screen.SignUpConfirmation.route)
    }
}

【讨论】:

  • 它正在工作,但是当我从设备上单击后退按钮时,屏幕从我的 SecondScreen 移动到 SignUp 屏幕,然后立即再次回到我的第二个屏幕。我需要快速点击多次才能返回注册屏幕
  • @Mehdi.ncb 你可以重置你的视图模型状态,例如使用DisposableEffect你可以等待注册屏幕消失。
  • @Mehdi.ncb 还可以查看this approach 以将单个操作从您的视图模型传递到视图
猜你喜欢
  • 2022-01-06
  • 2016-12-25
  • 2018-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
相关资源
最近更新 更多