Home > database >  Avoid duplicate call in LaunchEffect with multiple key in jetpack compose
Avoid duplicate call in LaunchEffect with multiple key in jetpack compose

Time:01-19

I want to avoid multiple function call when LaunchEffect key triggers.

LaunchedEffect(key1 = isEnableState, key2 = viewModel.uiState) {
     viewModel.scanState(bluetoothAdapter)
}

when first composition isEnableState and viewModel.uiState both will trigger twice and call viewModel.scanState(bluetoothAdapter).

isEnableState is a Boolean type and viewModel.uiState is sealed class of UI types.

var uiState by mutableStateOf<UIState>(UIState.Initial)
        private set 
var isEnableState by mutableStateOf(false)
        private set

So how can we handle idiomatic way to avoid duplicate calls?

Thanks

UPDATE

ContentStateful

@Composable
fun ContentStateful(
    context: Context = LocalContext.current,
    viewModel: ContentViewModel = koinViewModel(),
) {
    LaunchedEffect(key1 = viewModel.isEnableState, key2 = viewModel.uiState) {
        viewModel.scanState(bluetoothAdapter)
    }

    LaunchedEffect(viewModel.previous) {
        viewModel.changeDeviceSate()
    }
    ContentStateLess{
        viewModel.isEnableState = false
    }
}

ContentStateLess

@Composable
fun ContentStateLess(changeAction: () -> Unit) {
    Button(onClick = { changeAction() }) {
        Text(text = "Click On me")
    }
}

ContentViewModel

class ContentViewModel : BaseViewModel() {
    var uiState by mutableStateOf<UIState>(UIState.Initial)
    var isEnableState by mutableStateOf(false)
    
    fun scanState(bluetoothAdapter: BluetoothAdapter) {
        if (isEnableState && isInitialOrScanningUiState()) {
            // start scanning
        } else {
            // stop scanning
        }
    }

    private fun isInitialOrScanningUiState(): Boolean {
        return (uiState == UIState.Initial || uiState == UIState.ScanningDevice)
    }

    fun changeDeviceSate() {
        if (previous == BOND_NONE && newState == BONDING) {
            uiState = UIState.LoadingState
        } else if (previous == BONDING && newState == BONDED) {
            uiState = UIState.ConnectedState(it)
        } else {
            uiState = UIState.ConnectionFailedState
        }
    }
}

scanState function is start and stop scanning of devices.

CodePudding user response:

I guess the answer below would work or might require some modification to work but logic for preventing double clicks can be used only if you wish to prevent actions happen initially within time frame of small interval. To prevent double clicks you you set current time and check again if the time is above threshold to invoke click callback. In your situation also adding states with delay might solve the issue.

IDLE, BUSY, READY


var launchState by remember {mutableStateOf(IDLE)}
LaunchedEffect(key1 = isEnableState, key2 = viewModel.uiState) {
     if(launchState != BUSY){
          viewModel.scanState(bluetoothAdapter)
          if(launchState == IDLE){ launchState = BUSY)
     }
}



LaunchedEffect(launchState) {
     if(launchState == BUSY){
           delay(50)
           launchState = READY
     }
}
  • Related