英文:
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
中,我希望有一个依赖于email
的emailError
状态。每当email
发生更改(或其他事件,如焦点变化)时,我希望重新计算emailError
状态并强制重新组合Composable
。
在CB
中,如果我使用derivedStateOf
或mutableStateOf
而不使用remember
,我会收到类似于此的 linter 错误:
我可以通过添加@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)
}
... 其余代码,使用 focusedTextField、email 和 emailError 值呈现可组合内容
ComposableA
.... 维护状态并调用 CB
fun CA(
emailValue = emailValue,
onEmailChanged = { vm.newUiState(copy(emailValue = it)) },
focusedTextField = focusedTextField,
onFocusedTextField = { focusedTextField = it }
)
我可以将错误作为参数之一,并在CA
中提升状态,但我试图避免这样做,因为我希望尽可能使CB
保持独立。
- 我尝试了几个选项:使用
mutableStateOf
、derivedStateOf
和rememberUpdateState
。 - 我期望得到方向性的帮助。关于使用哪个类/函数的建议。
英文:
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:
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!
- I have tried several options: using
mutableStateOf
,derivedStateOf
andrememberUpdateState
. - 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论