Hello Everyone , Why remember with mutable state h...
# compose
p
Hello Everyone , Why remember with mutable state has different behaviour when using with key compared to without:- Without key is straight forward, any change in state will cause recomposition. With key:- It will only cause recomposition if the key is changing, if we are directly updating the state it will not cause recomposition. ( One exception - It does cause recomposition for state update as well till the key changed from what it initially was) Seems a bit weird or am i missing something?
p
With key: it will only cause recomposition if key is changing I haven't noticed that.
remember
and
mutableState
are 2 separate things.
remember
just keep alive the same instance of whatever you pass through the different recomposition cycles. If the remember function position in the composable tree does not change in the recomposition it keeps the same value passed before. Even if you pass another value from the composable function parameters it will keep the initial. To change this behavior and being able to update the remembered value you use a key, to instruct remember that it needs to update when the key updates. In general using keys is a good practice MutableState, is the actual element that causes recomposition, remember has nothing to do with triggering recomposition. MutableState always triggers recomposition when the value it enclosed changes.
t
Maybe it is better to use
rememberUpdateState(key)
in stead of
remember(key) { mutableStateOf(key) }
. But i am also a little bit unsure. Here is the documentation: https://developer.android.com/jetpack/compose/side-effects#rememberupdatedstate Maybe some one knows more?
p
RememberUpdateState is made for a specific situation where you want keys to update their value but you don't want to trigger execution in the effects depending on those keys.
p
I feel there is some misunderstanding,Will explain further var tabPosition by remember { _mutableStateOf_(if (currentMcqPosition <= 0) 0 else currentMcqPosition / STEP_SIZE) } Above code work as expected , u update the value and will cause reomposition Now if u change it to :- var tabPosition by remember(items) { _mutableStateOf_(if (currentMcqPosition <= 0) 0 else currentMcqPosition / STEP_SIZE) } As u can see, i added one key to remember,Now what will happen is , It will only cause recomposition if we update items which is the key, But if we do something like tabPosition = 5, this will cause no recomposition ** Above code will cause recomposition till "items" which is the key get's changed , and after that it will not cause recomp when updating value directly
@Timo Drick rememberUpdatedState has a totally separate usecase from what i know
p
I don’t know but it does not seems intuitive to me that behavior. How are you updating the
tabPosition
, from what scope. It might be the case of a stale reference to a mutableState. When the remember key changes it will give you a new reference to a new MutableState instance. Are you sure you update your lambdas with references to this new MutableState and not keep the references to the old instance. Calling on the old one won’t have effect(won’t cause recomposition).
p
I think u are correct and looking in the right direction, i am trying to update the tabPosition from from inside a flow collection and maybe that is having reference of old mutable state,Something like this:-
Copy code
var tabPosition by remember(items) { mutableStateOf(if (currentMcqPosition <= 0) 0 else currentMcqPosition / STEP_SIZE) }

LaunchedEffect(state) {
    snapshotFlow { state.firstVisibleItemIndex }.collect {
        if (state.firstVisibleItemIndex == 0) return@collect
        tabPosition = (state.firstVisibleItemIndex / STEP_SIZE)
    }
}
But even then i feel this problem should not happen , If we use a remember with key and if we are changing that from some LaunchedEffect or anywhere else, should still work Now it looks like i will have to fall back to rememberUpdatedState for this.
p
What about using items in the LaunchEffect keys. Is that work.
p
That’s what i ended up doing,Use remember without key and use LaunchedEfect with key to update the state, seems simpler But still it is hassle , should not have to be this way i feel
p
remember is always good with key. Because otherwise there is another trap, positional memoization. If the recomposition happens, and by coincidence, there are some remember call sites in the new tree that match the same position and type of the old tree. The remember won't be updated to the new value but is kept with the old one. I would key 🔐 both captures, the remember and the LaunchEffect. Perhaps as you mentioned before, is better to keep that state outside the composable cluster, in a State or ViewModel and you don't have to worry about all these lambda capture nightmare
I normally keep all the state possible outside of the composable functions. The composable just consume the state to render it and trigger actions on the state. But I try to not keep references to any state inside the composables
p
I do the same but at times we do end up creating reusable UI components and such calculations which are specific to that UI , i mean we will kinda have to keep it in that composable. But i get your point
👍 1
333 Views