Избегайте дублирования вызова в LaunchEffect с несколькими клавишами при создании реактивного ранца

Я хочу избежать вызова нескольких функций при срабатывании клавиши LaunchEffect.

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

при первой композиции isEnableState и viewModel.uiState сработают дважды и вызовут viewModel.scanState(bluetoothAdapter).

isEnableState — это тип Boolean, а viewModel.uiState — закрытый класс типов пользовательского интерфейса.

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

Итак, как мы можем справиться с идиоматическим способом, чтобы избежать дублирования вызовов?

Спасибо

ОБНОВЛЯТЬ

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
    }
}

Контентстателесс

@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 функция запуска и остановки сканирования устройств.

Добавьте условие if внутри блока.

Gabriele Mariotti 17.01.2023 15:05

что нужно проверить внутри, если блок?

Vivek Modi 17.01.2023 15:14

Вы используете 2 ключа, и они работают по назначению. Чего вы хотите избежать? Существуют ли некоторые состояния или комбинации, в которых не следует вызывать viewModel.scanState?

Gabriele Mariotti 17.01.2023 15:20

@Gabriele Mariotti isEnableState меняется в каком-то состоянии, и я хочу вызвать scanState. uiState постоянно меняется, а также хочет активировать функцию. Моя основная проблема заключается в том, что когда происходит 1-я композиция, я не хочу дважды вызывать функцию scanState. Я использовал оба ключа, потому что всякий раз, когда значение переменной изменяется, я хочу вызвать scanState, но только один раз. Если вы не понимаете, спросите меня, я добавлю больше кода в свой пример.

Vivek Modi 17.01.2023 15:29

Кажется, что оба ключа зависят друг от друга. Они меняются отдельно?

Steyrix 17.01.2023 15:32

@Steyrix да, они меняются отдельно..

Vivek Modi 17.01.2023 15:33

Что делает scanState?

Steyrix 17.01.2023 15:33

Это просто функция запуска и сканирования в фоновом режиме. я добавил свой код

Vivek Modi 17.01.2023 15:37

Можете ли вы опубликовать полный код составной функции, в которой вы вызываете эффект запуска?

Steyrix 17.01.2023 15:58

Полный код будет невозможен, вместо этого я добавлю соответствующий код.

Vivek Modi 17.01.2023 16:12

@Steyrix Я добавил весь код, который использует isEnableState и uiState..

Vivek Modi 17.01.2023 16:20

Не знаю, связано ли это, но кажется невозможным, чтобы это условие когда-либо было правдой. previous == BOND_NONE && previous == BONDING То же самое с условием else-if.

Tenfour04 17.01.2023 16:29

@ Tenfour04 хороший улов, я забыл изменить переменную. Теперь это исправлено, пожалуйста, посмотрите.

Vivek Modi 17.01.2023 16:35
1
13
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я предполагаю, что приведенный ниже ответ будет работать или может потребовать некоторой модификации для работы, но логика для предотвращения двойных щелчков может использоваться только в том случае, если вы хотите, чтобы действия не происходили изначально в течение небольшого интервала времени. Чтобы предотвратить двойные щелчки, вы устанавливаете текущее время и снова проверяете, превышает ли время пороговое значение для вызова обратного вызова щелчка. В вашей ситуации также может решить проблему добавление состояний с задержкой.

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
     }
}

Можно ли вызвать запущенный эффект с scanState второй раз? Он должен перейти из READY в BUSY.

Steyrix 18.01.2023 11:18

launchState переходит в состояние IDLE, BUSY только при первой композиции и переходит в состояние READY через 50 мс или с определенной задержкой. Состояние ON READY после запуска работает как в вопросе. Он действует только как временный блокировщик в течение определенного времени.

Thracian 18.01.2023 11:51

@Thracian идеально работает, как и ожидалось. Спасибо

Vivek Modi 18.01.2023 12:38

Другие вопросы по теме