StackerSapper
StackerSapper

Reputation: 251

Jetpack Compose - changing variable in a viewModel causes changes to the viewModel state?

I will describe my problem and then I'll provide my implementation. I have a viewModel with a variable connectionStatus that stores the value that tracks the connectionStatus of a "device", and changes when the connection status of the device changes.

I display the conneciton status in a Text composable. below the text cmposable, I also have a button "cancel connection", which calls the cancelConnection method in the viewModel.

the problem: every time the connection status is changing, the conectionStatus variable changes, which makes a recomposition for the Text composable (this is ok, expected behavior), but it also cause the button to be recomposed, even though nothing changed in the button state.

now, I think the problem is that the connectionState cause the state for the whole viewModel to be changed, because when I removed the Text composable, and left with only the button composable, it still was recomposed every time the connectionStatus was changed.

This is my implemntation: ViewModel:

class ConnectableTopAppBarViewModel(
    private val connectControllerUseCase: ConnectControllerUseCase = ConnectControllerUseCase()
) :
    ViewModel() {

    private val _uiState = MutableStateFlow(ConnectableTopAppBarUiState())
    val uiState: StateFlow<ConnectableTopAppBarUiState>
        get() = _uiState.asStateFlow()

    private val _connectionStatus = mutableStateOf(ConnectionStatus.AWAIT_CONNECTION)
    val connectionStatus get() = _connectionStatus
    
    private var job = Job()
        get() {
            if (field.isCancelled) field = Job()
            return field
        }

    init {
        viewModelScope.launch {
            connectControllerUseCase.getConnectionStatus().collect { newStatus ->
                _connectionStatus.value = newStatus
            }
        }
    }

    fun cancelConnection(deviceIp: String) {
        job.cancel()
        connectControllerUseCase.cancelConnection()
    }
}

now in my composables I have sometihng like this:

val connectionStatus by viewModel.connectionStatus
...
Text(text = connectionStatus)
...
CustomButton(cancelAction = viewModel::cancelConnection)
...
CustomButton(
    cancelAction = (String) -> ()
) {
    Button(onClick = { cancelAction("test") })
}

what am I missing here? why CustomButton is being recomposed every time the connectionStatus is changed? even though I'm passing method refference.

Upvotes: 2

Views: 817

Answers (1)

gabilcious
gabilcious

Reputation: 46

Try:

CustomButton(cancelAction = remember { viewModel::cancelConnection } )

Upvotes: 2

Related Questions