Home > Software engineering >  _uiState.value = uiState.value.copy() don't cause recompose
_uiState.value = uiState.value.copy() don't cause recompose

Time:07-25

There is some unresolved situation that happens with my code, my target is to change the property of an item in the "devices" list (update a boolean), The change should cause recompose of the view, but that doesn't happen, In addition, I can see that the item indeed changes with the debugger, but it also causes to add an additional item(an old copy without the included change) to show up in my list.

Is there any idea how I was wrong with the new value assignment?

The ViewModel

private val _uiState = mutableStateOf(BTPairingState())
val uiState: State<BTPairingState> get() = _uiState 

This is how i edit the item in the list

    if (handlerDeviceResponse.status != null) {            
        viewModelScope.launch {
            uiState.value.devices.find { it.macAddress == handlerDeviceResponse.device.macAddress }?.isConnected = handlerDeviceResponse.status
            _uiState.value = uiState.value.copy()
        }
    }

BTPairingState:

data class BTPairingState (
    val devices: MutableList<BtDeviceItemUiModel> = mutableListOf(),
    val deviceType: DeviceType = DeviceType.RFID,
)

The Data class

data class BtDeviceItemUiModel(
    val name: String,
    val macAddress : String,
    var isConnected: Boolean = false
)

The Screen:

@Destination
@Composable
fun BTPairScreen(
    viewModel: BTPairViewModel = hiltViewModel(),
) {

    val state = viewModel.uiState
    BTPairDevices(state.value.devices) { viewModel.deviceItemClicked(it) }


CodePudding user response:

Replace var isConnected by val isConnected.

Then, replace val devices: MutableList<BtDeviceItemUiModel> = mutableListOf() by val devices: List<BtDeviceItemUiModel> = emptyList().

IOW, stop using mutable values inside of your state.

Then, you can revise your code to update your MutableState with a new value, using something like:

    if (handlerDeviceResponse.status != null) {            
            val newDevices = uiState.value.devices.map { device ->
              if (device.macAddress == handlerDeviceResponse.device.macAddress) {
                device.copy(isConnected = handlerDeviceResponse.status)
              } else {
                it
              }
            }
            _uiState.value = uiState.value.copy(devices = newDevices)
    }

CodePudding user response:

In your viewmodel Change state to StateFlow

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

In your BTPairScreen Instead of

val state = viewModel.uiState

Use:

val state by viewModel.state.collectAsState()
  • Related