アプリ開発備忘録

PlayStationMobile、Android、UWPの開発備忘録

【Jetpack Compose】derivedStateOfとremember(key)の使い所

派生Stateである DerivedState を作成する derivedStateOf の使い所についてです。

基本的な使い方

rememberの中で derivedStateOf を使います。その中で State を読み込みます。
これにより、 Stateの counter1 が変化した時にだけ計算が行われます。

var counter1 by remember { mutableStateOf(0) }

val counter1MoreThan1 by remember {
    derivedStateOf {
        counter1 >= 1
    }
}

動作確認

counter2を用意しました。 counter1をニ回増やした後に、counter2をニ回増やします。

@Composable
private fun Content() {
    Column {
        var counter1 by remember { mutableStateOf(0) }
        var counter2 by remember { mutableStateOf(0) }

        val counter1MoreThan1 by remember {
            derivedStateOf {
                println("call derivedStateOf")
                counter1 >= 1
            }
        }

        LaunchedEffect(Unit) {
            delay(1000)
            counter1++
            delay(1000)
            counter1++
            delay(1000)
            counter2++
            delay(1000)
            counter2++
        }

        Spacer(modifier = Modifier.width(12.dp))

        Text("counter1=$counter1, counter2=$counter2")
        Text("counter1 more than 1 = $counter1MoreThan1")
    }
}

counter2が増えたときには呼ばれていない事がわかります。

call derivedStateOf
before call cunter1++
call derivedStateOf
before call cunter1++
call derivedStateOf
before call counter2++
before call counter2++

これは、 derivedStateOf のLambdaの中で、どのStateObjectを読み込んでいるか保存して、変更された事を検知し、変更された時だけ再計算を行うことで実現しています。
https://matsudamper.hatenablog.com/entry/2022/06/28/154855

remember(key) の使い方

derivedStateOf は派生Stateであるので、変更はStateObjectしか検知してくれません。

引数が変更された時だけ実行したいだけの場合は remember(key) を使います。キーが変更された時にだけ中が実行されます。

@Composable
private fun Content(uiState: UiState) {
    Column {
        val moreThan1 = remember(uiState) { 
            uiState.counter >= 1
        }

        Text("counter1 more than 1 = $moreThan1")
    }
}

ここではcounterしか読み込んでいないので、実際はcounterだけをkeyにすると更に良いです。

@Composable
private fun Content(uiState: UiState) {
    Column {
        val moreThan1 = remember(uiState.counter) {
            uiState.counter >= 1
        }

        Text("counter1 more than 1 = $moreThan1")
    }
}