Jetpack Compose:`derivedStateOf` 依赖于可组合函数参数的部分

huangapple go评论59阅读模式
英文:

Jetpack Compose: `derivedStateOf` that depends on a composable function parameter

问题

Sure, here's the translated portion of your text:

所以,我现在面临一个有趣的情况:

ComposableA(CA)调用ComposableB(CB)并传递一个名为email的参数。在ComposableB中,我希望有一个依赖于emailemailError状态。每当email发生更改(或其他事件,如焦点变化)时,我希望重新计算emailError状态并强制重新组合Composable

CB中,如果我使用derivedStateOfmutableStateOf而不使用remember,我会收到类似于此的 linter 错误:

Jetpack Compose:`derivedStateOf` 依赖于可组合函数参数的部分

我可以通过添加@SuppressLint("UnrememberedMutableState")注释来抑制此错误,但即使焦点或电子邮件发生更改,这也不会强制重新计算状态。如果只是使用变量,Compose 引擎将不知道可组合 UI 依赖于该值,即使值发生更改,也不会重新组合它。

以下是代码:

ComposableB

fun CB(
  emailValue: TextFieldValue = TextFieldValue("", TextRange.Zero),
  onEmailChanged: (emailValue: TextFieldValue) -> Unit = {},
  focusedTextField: FeatTextField = FeatTextField.Email,
  onFocusedTextField: (textField: FeatTextField) -> Unit = {}
) {
  // 错误源自 emailAddress
  val emailError by derivedStateOf {
    Timber.tag("StateTest").i("EmailValue: ${emailValue.text}")
    focusedTextField != SignInTextField.Email && !Validator.validateEmail(emailValue.text)
  }

  ... 其余代码使用 focusedTextFieldemail 和 emailError 值呈现可组合内容

ComposableA

.... 维护状态并调用 CB

fun CA(
  emailValue = emailValue,
  onEmailChanged = { vm.newUiState(copy(emailValue = it)) },
  focusedTextField = focusedTextField,
  onFocusedTextField = { focusedTextField = it }
)

我可以将错误作为参数之一,并在CA中提升状态,但我试图避免这样做,因为我希望尽可能使CB保持独立。

  1. 我尝试了几个选项:使用mutableStateOfderivedStateOfrememberUpdateState
  2. 我期望得到方向性的帮助。关于使用哪个类/函数的建议。
英文:

So, here is an interesting situation I am in:

ComposableA (CA) calls ComposableB (CB) with a parameter email. In ComposableB, I want to have an emailError state that depends on email. Everytime the email changes (or some other event like FocusChange), I want to recompute the emailError state and force a recomposition of the Composable.

In CB, If I use derivedStateOf or mutableStateOf without remember, I get a linter error like this:

Jetpack Compose:`derivedStateOf` 依赖于可组合函数参数的部分

I can suppress that by adding @SuppressLint("UnrememberedMutableState") annotation but that doesn't force the state to be recomputed even when the focus or email changes. If I just use a variable, the compose engine wouldn't know that the composable UI depends on that value and would not recompose it even if there is a change in value.

Here is the code:

ComposableB

fun CB(
  emailValue: TextFieldValue = TextFieldValue("", TextRange.Zero),
  onEmailChanged: (emailValue: TextFieldValue) -> Unit = {},
  focusedTextField: FeatTextField = FeatTextField.Email,
  onFocusedTextField: (textField: FeatTextField) -> Unit = {}
) {
  // Error is derived from emailAddress
  val emailError by derivedStateOf {
    Timber.tag("StateTest").i("EmailValue: ${emailValue.text}")
    focusedTextField != SignInTextField.Email && !Validator.validateEmail(emailValue.text)
  }

  ... Rest of the code that renders the composable using focusedTextField, email and emailError values

ComposableA

.... Maintains the state and calls CB

fun CA(
  emailValue = emailValue,
  onEmailChanged = { vm.newUiState(copy(emailValue = it)) },
  focusedTextField = focusedTextField,
  onFocusedTextField = { focusedTextField = it }
)

I can make error as one of the parameters and hoist the state in CA but I am trying to avoid that because I want to keep CB as independent as possible.

Any pointers is appreciated.

Thank you!

  1. I have tried several options: using mutableStateOf, derivedStateOf and rememberUpdateState.
  2. I am expecting a directional help. Suggestions around which is the right Class/Function to use

答案1

得分: 4

The short answer is that you shouldn't be using derivedStateOf and probably don't even need remember.

derivedStateOf should only be used if the expression reads mutable state values. If no mutable state values are read, then the derivedStateOf is providing no value and is overhead you don't need. Also, derivedStateOf is only really useful if the value produced will change less frequently than the data it reads.

If the parameter values are not changing frequently, then the normal skipping of a composable function might be all you need here. CB will only execute when one of its parameters change (assuming they are all stable).

If, however, one of the parameters is changing a lot and is not part of the expensive calculation, consider using remember for the expensive calculation. Be sure to pass in the values that are changing as parameters to remember so Compose knows when to re-evaluate the remembered expression. For example,

val emailError by remember(emailValue.text, focusedTextField) {
    Timber.tag("StateTest").i("EmailValue: ${emailValue.text}")
    focusedTextField != SignInTextField.Email && !Validator.validateEmail(emailValue.text)
}

However, as noted above, since these two parameters are the only ones likely to be changing, the skipping Compose is already doing might be enough.

The article https://stefma.medium.com/jetpack-compose-remember-mutablestateof-derivedstateof-and-remembersaveable-explained-270dbaa61b8 goes into this in more detail.

英文:

The short answer is that you shouldn't be using derivedStateOf and probably don't even need remember.

derivedStateOf should only be used if the expression reads mutable state values. If no mutable state values are read, then the derivedStateOf is providing no value and is overhead you don't need. Also, derivedStateOf is only really useful if the value produced will change less frequently than the data it reads.

If the parameter values are not changing frequently then the normal skipping of a composable function might be all you need here. CB will only execute when one of its parameters change (assuming they are all stable).

If, however, one of the parameters is changing a lot and is not part of the expensive calculation, consider using remember for the expensive calculation. Be sure to pass in the values that are changing as parameters to remember so Compose knows when to re-evaluate the remembered expression. For example,

val emailError by remember(emailValue.text, focusedTextField) {
    Timber.tag("StateTest").i("EmailValue: ${emailValue.text}")
    focusedTextField != SignInTextField.Email && !Validator.validateEmail(emailValue.text)
  }

However, as noted above, since these two parameter are the only ones likely to be changing, the skipping Compose is already doing might be enough.

The article https://stefma.medium.com/jetpack-compose-remember-mutablestateof-derivedstateof-and-remembersaveable-explained-270dbaa61b8 goes into this in more detail.

huangapple
  • 本文由 发表于 2023年6月12日 20:55:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456879.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定