-
-
Notifications
You must be signed in to change notification settings - Fork 755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rule suggestion: In Compose, forbid var
with mutableStateOf
(whether wrapped in remember
or not)
#4501
Comments
We have a similar rule |
That's a nice addition but really Compose specific (as the |
Could we make the rule no Android dependant? I mean, allow to setup a list of mutable classes and forbid to store those in a |
In general, this makes a lot of sense to me. However, one complexity with Compose specifically is that often state is wrapped in a
Just one correction here -- Compose, thanks to JB, is now on Desktop and Web as well :-) However, your wider point is taken. |
That is not a problem if the rule is extended to allow a list of mutable types. I'm more concerned that this will potentially overcomplicate the rule. Happy to see a PR if someone wants to jump on it though. |
The other option is to create a separate rule "DoubleMutabilityForState", which would work very similarly to "DoubleMutabilityForCollection", except that it would contain a list of known state types that introduce double mutability. It doesn't have to be configurable, though there are other rules that are configurable in similar ways e.g. "WildcardImport" so I don't personally see that as a big deal in terms of complexity. |
Yup you're right. I recalled the rule being quite complicated. However it's fairly simple. I don't think we need a new rule, we can just make this list configurable. Do you want to try to submit a PR for it? Lines 69 to 78 in 58e95e6
|
The main reason I'd lean towards creating a new rule is that the name Plus, a user may very well want to enable or disable these in isolation i.e. keep |
I think this can be easily handled using the
That can be achieved by customizing the list of mutable types. |
PR submitted. |
Looking at the following code sample from https://developer.android.com/jetpack/compose/state#viewmodels-source-of-truth data class ExampleUiState(
dataToDisplayOnScreen: List<Example> = emptyList(),
userMessages: List<Message> = emptyList(),
loading: Boolean = false
)
class ExampleViewModel(
private val repository: MyRepository,
private val savedState: SavedStateHandle
) : ViewModel() {
var uiState by mutableStateOf<ExampleUiState>(...)
private set
// Business logic
fun somethingRelatedToBusinessLogic() { ... }
}
@Composable
fun ExampleScreen(viewModel: ExampleViewModel = viewModel()) {
val uiState = viewModel.uiState
...
Button(onClick = { viewModel.somethingRelatedToBusinessLogic() }) {
Text("Do something")
}
} Would this rule discourage the use of this |
Currently not. The rule would not even flag this specific case as it's just checking for assignments |
Is this a proposal to encourage destructuring declaration over the other methods of declaring If so, we might want to pause on that since the destructuring declaration is being considered as a foot-gun by some in the community + compose team, especially when used inside Composables with References: The issuetracker issue's latest comment from the team wonders if the destructuring syntax implementation is currently a bug or not. |
Hmm, I actually just realized that is true.
My original intent was indeed to encourage either Option 1 or Option 3 below, over Option 2 which uses a
My initial thinking when I saw Option 2 was "yuck" because Hopefully the Compose team can get this sorted out and provide better recommendations / documentation / api improvements. The last comment in the issue tracker that the destructuring declaration should probably return a getter, not the current value, is not a knock on the destructuring approach per say, but rather than component1() value returned from it is a value rather than a function. What this discussion tells me is that any Detekt rules that target Compose state usage should wait until the preferred idioms are sorted out. All that being said, the PR being implemented here for now cares nothing for any of this because all it does is make the |
I think a rule that prevents the following makes total sense: var value1 = mutableStateOf(default)
// as well as
var value2 = remember { mutableStateOf(default) } The reason being because the above assign a mutable type to a mutable variable/field, hence the double mutability. However discouraging the use of var value1 by mutableStateOf(default)
// or
var value2 by remember { mutableStateOf(default) } seems like we are fighting against a valuable language feature. Not only Compose, but Kotlin also has its own built-in delegates, such as |
Agreed. Hopefully updates to that issue will add clarity.
Agreed. Additionally, this one is also problematic (explained here): // DON'T
var list by mutableStateOf(mutableListOf("a")) // changing inner mutable list will not update state
// Or, Worse
var list = mutableStateOf(mutableListOf("a"))
// DO
var list by mutableStateOf(listOf("a"))
// OR
val list = mutableStateListOf("a") Update: Given that^, to prevent the following, not sure if a custom android lint rule is better suited (at least right now you still get warned if var value1 = remember { mutableStateOf("default") } Regardless, if we're adding any new customizability to rules related to Compose, then probably good to also update https://detekt.github.io/detekt/compose.html so that folks can see all Compose-related detekt things in one place. |
As a rule of thumbs, we're trying to keep the tool as platform/framework-agnostic as possible. We're generally ok making changes to rules to make them more extensible and easier to adapt to work with a specific framework (e.g., make a rule configurable to work with Jetpack Compose). However, if you need a specific detection logic for a use case like the one you're suggesting @drinkthestars, that would probably better fit a custom rule. |
@drinkthestars At this point pretty unrelated to this issue, but for interest sake: part of that Twitter thread has comments from Leland Richardson which are relevant. Example:
|
Expected Behavior of the rule
In Compose, MutableState can be accessed in a variety of ways, see https://developer.android.com/jetpack/compose/state#state-in-composables.
One of these ways is to declare the
MutableState
as avar
and then access the state value directly via property delegation:However, this introduces the use of
var
s into Composables, which can easily be avoided by deconstructing the getter and setter properties and declaring them asval
:Examples that would trigger the rule:
Context
I believe this results in cleaner code and better compile-time safety through the use of
val
s rather thanvar
s.The text was updated successfully, but these errors were encountered: